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)
AFAICT 您无法比较非常量引用变体之间的相等性,因为在 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
类型。
以下程序中止:
#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)
AFAICT 您无法比较非常量引用变体之间的相等性,因为在 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
类型。