Boost 引用变体和相等性比较

Boost variant of references and equality comparison

以下程序中止:

#include <boost/variant.hpp>

using variant_type = boost::variant< int&, std::string& >;

int main () {
    int a, b;

    variant_type v (a), u (b);
    v == u;

    return 0;
}

与:

$ g++ -std=c++14 t.cpp && ./a.out 
a.out: /opt/boost/default/include/boost/variant/detail/forced_return.hpp:39: T boost::detail::variant::forced_return() [with T = const int&]: Assertion `false' failed.
Aborted (core dumped)

A​​FAICT 您无法比较非常量引用变体之间的相等性,因为在 class known_get 中选择了错误的运算符函数调用重载。 known_get 在比较器访问者中为 const T 实例化,而不是似乎应该是 T 的实例(v1.59.0 中的 variant.hpp:905)。

我是不是漏掉了什么?

好的,我考虑了一下(并再次查看了文档)。

boost::variant 的概要未显示为变体定义的运算符==。

这让我建议正确的比较方法是通过二进制访问者。

这里又是你的(修改过的)代码,它在我的机器上编译(apple clang)你的代码也让我的编译器崩溃了。

#include <string>
#include <boost/variant.hpp>

using variant_type = boost::variant< int&, std::string& >;

struct is_equal
{
    // compare int with int
    bool operator()(const int& l, const int& r) const {
        return l == r;
    }

    // compare string with string
    bool operator()(const std::string& l, const std::string& r) const {
        return l == r;
    }

    // anything else compared with anything else compares false.        
    template<class X, class Y>
    bool operator()(const X&, const Y&) const {
        return false;
    }
};

int main () {
    int a = 0, b = 0;

    variant_type v = a, u = b;

    bool ok = boost::apply_visitor(is_equal(), v, u);
//    bool ok = (v == u);

    return 0;
}

我认为这是一个 Boost 错误。

这里的type requirements是:

  • CopyConstructible or MoveConstructible.
  • Destructor upholds the no-throw exception-safety guarantee.
  • Complete at the point of variant template instantiation. (See boost::recursive_wrapper<T> for a type wrapper that accepts incomplete types to enable recursive variant types.)

以及:

  • EqualityComparable: variant is itself EqualityComparable if and only if every one of its bounded types meets the requirements of the concept.

引用类型是可复制构造的、不可抛出的可破坏的、完整的和相等的可比较的。所以我们在所有方面都做得很好。问题是实施中使用的访问者是:

template <typename T>
bool operator()(const T& rhs_content) const
{
    // Since the precondition ensures lhs and rhs types are same, get T...
    known_get<const T> getter;
    const T& lhs_content = lhs_.apply_visitor(getter);

    // ...and compare lhs and rhs contents:
    return Comp()(lhs_content, rhs_content);
}

也就是说,我们使用 const T 作为已知的 getter。这对于非引用类型很好,但对于引用类型不正确,因为 known_get 断言如果它得到错误的类型:

T& operator()(T& operand) const BOOST_NOEXCEPT
{
    return operand;
}

template <typename U>
T& operator()(U& ) const
{
    // logical error to be here: see precondition above
    BOOST_ASSERT(false);
    return ::boost::detail::variant::forced_return<T&>();
}

对于 int&,这些重载变为:

const int& operator()(const int& ) const;
const int& operator()(int& ) const; [ U = int ]

第二个重载是首选,因为它引用的类型比非模板重载更少 const 限定。这就是你得到断言的原因,而这种行为显然是不正确的。我们应该能够比较参考文献!

更简单的解决方案是从 comparer 中删除 const 并简单地使用:

template <typename T>
bool operator()(T& rhs_content) const
{
    known_get<T> getter;
    T& lhs_content = lhs_.apply_visitor(getter);

    return Comp()(lhs_content, rhs_content);
}

这适用于引用类型以及 const 类型。