我在代码中使用了很多变量,我需要在某些地方与内容进行比较,以测试变量的内容的价值。
例如:
if(equals<int>(aVariant, 0)){
//Something
} else {
//Something else
}
这个简单的模板函数我是为此而编写的:
template<typename V, typename T>
inline bool equals(V& variant, T value){
return boost::get<T>(&variant) && boost::get<T>(variant) == value;
}
这很好用,但代码开始难以阅读。我更喜欢使用这样的比较运算符:
if(aVariant == 0){
//Something
} else {
//Something else
}
但我无法提供有效的运营商实施。问题是==运算符已在变量中实现,在编译时失败...
有人知道实现它的方法吗?或者一种禁用此限制的方法?即使我必须为变体中包含的每种可能类型实现一个版本,这也不是问题。
由于
答案 0 :(得分:8)
正如评论的那样,我认为解决这个难题的最简洁方法是使用运营商策略(每个运营商,实际上)增强boost::variant<>
的实施,允许客户覆盖行为用于外部用途。 (显然这是很多通用的编程工作)。
我 已实施 解决方法。这使您可以为变体实现自定义运算符,即使它具有boost/variant.hpp
中实现的运算符。
我的脑波是使用BOOST_STRONG_TYPEDEF
。
我们的想法是通过制作不同实际类型的变体来打破重载决策(或者至少使我们的自定义重载成为首选分辨率)(它提醒了一点“绝望的”ADL障碍:你不能从作用域中 un - using
可见名称,并且你不能进入'非军事化名称空间'(障碍),因为冲突的声明驻留在类名称空间本身中;但您 可以 使他们不适用到您的'诱饵'类型。)
唉,这对operator<
和家庭来说效果不好,因为boost strong-typedef实际上很难用'base'类型保存(弱)总排序语义。在普通英语中:强类型定义operator<
以及(委托基类型的实现)。
不用担心,我们可以做一个CUSTOM_STRONG_TYPEDEF,并以我们的快乐方式。查看main中的测试用例以获取概念证明(下面的输出)。
由于所描述的有趣的互动,我为此演示选择了operator<
,但我认为没有任何方法可以让您的变体类型获得自定义operator==
的
#include <boost/variant.hpp>
#include <boost/lexical_cast.hpp>
#include <string>
#include <iostream>
/////////////////////////////////////////////////////
// copied and reduced from boost/strong_typedef.hpp
#define CUSTOM_STRONG_TYPEDEF(T, D) \
struct D \
/*: boost::totally_ordered1< D */ \
/*, boost::totally_ordered2< D, T */ \
/*> > */ \
{ \
T t; \
explicit D(const T t_) : t(t_) {}; \
D(){}; \
D(const D & t_) : t(t_.t){} \
D & operator=(const D & rhs) { t = rhs.t; return *this;} \
D & operator=(const T & rhs) { t = rhs; return *this;} \
operator const T & () const {return t; } \
operator T & () { return t; } \
/*bool operator==(const D & rhs) const { return t == rhs.t; } */\
/*bool operator<(const D & rhs) const { return t < rhs.t; } */\
};
namespace detail
{
typedef boost::variant<unsigned int, std::string> variant_t;
struct less_visitor : boost::static_visitor<bool>
{
bool operator()(const std::string& a, int b) const
{ return boost::lexical_cast<int>(a) < b; }
bool operator()(int a, const std::string& b) const
{ return a < boost::lexical_cast<int>(b); }
template <typename T>
bool operator()(const T& a, const T& b) const
{ return a < b; }
};
struct variant_less
{
less_visitor _helper;
bool operator()(const variant_t& a, const variant_t& b) const
{ return boost::apply_visitor(_helper, a, b); }
};
}
CUSTOM_STRONG_TYPEDEF(detail::variant_t, custom_vt);
namespace
{
bool operator<(const custom_vt& a, const custom_vt& b)
{ return detail::variant_less()(a, b); }
std::ostream& operator<<(std::ostream& os, const custom_vt& v)
{ return os << (const detail::variant_t&)v; }
}
int main()
{
const detail::variant_t I(43), S("42");
const custom_vt i(I), s(S);
// regression test (compare to boost behaviour)
std::cout << "boost: " << I << " < " << S << ": " << std::boolalpha << (I<S) << "\n";
std::cout << "boost: " << S << " < " << I << ": " << std::boolalpha << (S<I) << "\n";
// FIX1: clumsy syntax (works for boost native variants)
detail::variant_less pred;
std::cout << "clumsy: " << i << " < " << s << ": " << std::boolalpha << pred(i,s) << "\n";
std::cout << "clumsy: " << s << " < " << i << ": " << std::boolalpha << pred(s,i) << "\n";
std::cout << "clumsy: " << I << " < " << S << ": " << std::boolalpha << pred(I,S) << "\n";
std::cout << "clumsy: " << S << " < " << I << ": " << std::boolalpha << pred(S,I) << "\n";
// FIX2: neat syntax (requires a custom type wrapper)
std::cout << "custom: " << i << " < " << s << ": " << std::boolalpha << (i<s) << "\n";
std::cout << "custom: " << s << " < " << i << ": " << std::boolalpha << (s<i) << "\n";
}
输出:
boost: 43 < 42: true
boost: 42 < 43: false
clumsy: 43 < 42: false
clumsy: 42 < 43: true
clumsy: 43 < 42: false
clumsy: 42 < 43: true
custom: 43 < 42: false
custom: 42 < 43: true
当然,如果你想将custom_vt传递给使用TMP作用于变体的库API,可能会有不幸的相互作用。但是,由于两者之间无痛的转换,你应该能够在适当的时候使用detail :: variant_t来“战胜你的方式”。
这是您在通话网站获得语法便利所需支付的价格。