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

Bap*_*cht 4 c++ boost-variant

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

例如:

if(equals<int>(aVariant, 0)){
    //Something
} else {
    //Something else
}
Run Code Online (Sandbox Code Playgroud)

使用这个简单的模板函数,我为此目的写了:

template<typename V, typename T>
inline bool equals(V& variant, T value){
    return boost::get<T>(&variant) && boost::get<T>(variant) == value;
}
Run Code Online (Sandbox Code Playgroud)

这很好用,但代码开始难以阅读.我更喜欢使用这样的比较运算符:

if(aVariant == 0){
    //Something
} else {
    //Something else
}
Run Code Online (Sandbox Code Playgroud)

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

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

谢谢

seh*_*ehe 8

正如评论的那样,我认为解决这个难题的最简洁方法是boost::variant<>使用运营商策略(每个运营商,实际上)增强实施,允许客户端覆盖外部使用的行为.(显然这是很多通用的编程工作).

已经实施了一个解决方法.这使您可以为变体实现自定义运算符,即使它具有实现的变量boost/variant.hpp.

我的脑电波是用的BOOST_STRONG_TYPEDEF.

我们的想法是通过制作不同实际类型的变体来打破重载决策(或者至少使我们的自定义重载成为首选分辨率)(它提醒了一点"绝望的"ADL障碍:你不能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";

}
Run Code Online (Sandbox Code Playgroud)

输出:

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
Run Code Online (Sandbox Code Playgroud)

当然,如果你想将custom_vt传递给使用TMP作用于变体的库API,可能会有不幸的相互作用.但是,由于两者之间无痛的转换,你应该能够在适当的时候使用detail :: variant_t来"战胜你的方式".

这是您在呼叫站点获得语法便利所需支付的价格.