如何在包含类型的变体上使用比较运算符?

Posted

技术标签:

【中文标题】如何在包含类型的变体上使用比较运算符?【英文标题】:How to use comparison operators on variant with contained types? 【发布时间】:2012-02-05 21:33:21 【问题描述】:

我在我的代码中使用了很多变体,我需要与某些地方的内容进行比较,以测试变体的内容的价值。

例如:

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

但我无法提供运算符的有效实现。问题是 == 运算符已经在变体中实现,在编译时失败......

有人知道实现它的方法吗?还是一种禁用此限制的方法?即使我必须为变体中包含的每种可能类型实现一个版本,这也不是问题。

谢谢

【问题讨论】:

@R.MartinhoFernandes 我不认为他会得到他想要的语法。 哦,我不知道变体开发人员明确禁止这种情况。有谁知道这是什么原理? @R.MartinhoFernandes:没人想到吗?他们本可以使用traits 或比较policies。 @Baptiste,您可以修改变体类来做到这一点并将想法提交到提升列表 手册说:“重载返回 void 的存在只是为了禁止将运算符右侧隐式转换为变体;因此,它的使用将(故意)导致编译时错误。” - 你可以做if (aVariant == variant_type(0)),但我看不出用户定义的函数有什么问题(通常可以推断出参数类型)。 @UncleBens 我的用户定义函数没有问题,只是我的代码中的语法有点重。 【参考方案1】:

正如评论的那样,我认为解决这个难题的最简洁的方法是使用允许客户端覆盖外部行为的操作员策略(实际上是每个操作员)来增强 boost::variant&lt;&gt; 的实施用途。 (显然这是很多通用编程工作)。

已实施一种解决方法。这使您可以为 variants 实现自定义运算符,即使它已在 boost/variant.hpp 中实现。

我的脑电波是使用BOOST_STRONG_TYPEDEF

我们的想法是通过使我们的变体成为不同的实际类型来打破重载解决方案(或至少使我们的自定义重载成为首选解决方案)(它有点让人想起“绝望”的 ADL 障碍:你不能un-using在一个范围内可见的名字,你不能去一个“非军事化命名空间”(障碍),因为冲突的声明驻留在类命名空间本身中 em>;但你可以让它们不适用到你的“诱饵”类型)。

唉,这对operator&lt; 和家庭来说效果不佳,因为 boost strong-typedef 实际上很难使用“base”类型来保留(弱)总排序语义。用普通英语:强类型定义定义operator&lt; 以及委托给基类型的实现)。

不用担心,我们可以执行 CUSTOM_STRONG_TYPEDEF 并继续我们的快乐之路。查看 main 中的测试用例以进行概念验证(下面的输出)。

由于所描述的有趣的交互,我为这个演示选择了operator&lt;,但我想你不会有什么办法让自定义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 来“争取”。

这是您在呼叫站点获得语法便利所必须付出的代价。

【讨论】:

它工作得很好:) 我只需要添加一些函数就可以像自定义类型的变体一样工作。非常感谢。

以上是关于如何在包含类型的变体上使用比较运算符?的主要内容,如果未能解决你的问题,请参考以下文章

equals方法的使用

JavaScript 关系比较运算符如何强制类型?

如何比较Oracle中的复杂集合?

如何在带有 select 语句的 where 子句中使用比较运算符?

如何使用布尔和字符串数据类型之间的比较运算符从数据框中过滤数据

如何比较 TypeScript 中的枚举