如何在包含类型的变量上使用比较运算符? [英] How to use comparison operators on variant with contained types?

查看:78
本文介绍了如何在包含类型的变量上使用比较运算符?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

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

I'm using variant a lot in my code and I need to make comparisons with the content in some places to test the content of the variant for its value.

例如:

if(equals<int>(aVariant, 0)){
    //Something
} else {
    //Something else
}

我为此编写了这个简单的模板函数:

with this simple template function I've written for this purpose:

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

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

This works well, but the code starts to be difficult to read. I prefer to use comparison operators like that:

if(aVariant == 0){
    //Something
} else {
    //Something else
}

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

But I wasn't able to come with a valid implementation of the operator. The problem is that the == operator has already been implemented in the variant to fails at compile-time...

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

Do someone know a way to implement it anyway ? Or a way to disable this limitation ? Even if I have to implement a version for each possible type contained in the variant, that's not a problem.

谢谢

推荐答案

我认为,最干净的方法解决此难题的方法是通过允许客户覆盖的运营商策略(实际上是针对每个运营商)来增强 boost :: variant<> 的实现。 em>外部使用行为。 (显然,这是很多通用的编程工作。)

As commented, I think the cleanest way to solve this conundrum would be to enhance the implementation of boost::variant<> with an operator policy (per operator, really) that allows clients to override behaviour for external uses. (Obviously that is a lot of generic programming work).

已实现 解决方法。这样,即使在 boost / variant.hpp 中实现了 variant 的自定义运算符,也可以使用它。

I have implemented a workaround. This lets you implement custom operators for variants even when it has those implemented in boost/variant.hpp.

我的头脑是使用 BOOST_STRONG_TYPEDEF

My brainwave was to use BOOST_STRONG_TYPEDEF.

这个想法是通过使我们的 actual类型不同的变体来打破重载分辨率(或至少使我们的自定义重载成为首选的分辨率)。 (这让我们想起了一个绝望的 ADL障碍:您不能 un-使用 范围中的可见名称,并且不能转到非军事命名空间(障碍),因为冲突的声明驻留在类命名空间本身中 ;但是您 可以 将其设置为 not-

The idea is to break overload resolution (or at least make our custom overloads the preferred resolution) by making our variants of a different actual type (it reminds a bit of a 'desperate' ADL barrier: you cannot un-using visible names from a scope, and you cannot go to a 'demilitarized namespace' (the barrier) since the conflicting declarations reside in the class namespace itself; but you can make them not-apply to your 'decoy' type).

A,对于 operator <来说效果不佳$ c>和family,因为boost strong-typedef实际上很努力地维护(弱)'base'类型的总排序语义。用普通英语:强类型定义也定义 operator< 代表基本类型的实现)。

Alas, that won't work very well for operator< and family, because boost strong-typedef actually works hard to preserve (weak) total ordering semantics with the 'base' type. In normal English: strong typedefs define operator< as well (delegating to the base type's implementation).

不用担心,我们可以做一个CUSTOM_STRONG_TYPEDEF并按照我们的喜好来做。查看main中的测试用例以进行概念验证(下面的输出)。

Not to worry, we can do a CUSTOM_STRONG_TYPEDEF and be on our merry way. Look at the test cases in main for proof of concept (output below).

由于所描述的有趣的交互作用,我选择了 operator< ; 进行此演示,但我想您将无法通过任何方式获得用于变体类型的自定义 operator ==

Due to the interesting interactions described, I picked operator< for this demo, but I suppose there wouldn't be anything in your way to get a custom operator== going for your variant types.

#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来拼搏。

Now of course, there might be unfortunate interactions if you want to pass your custom_vt into library API's that use TMP to act on variants. However, due to the painless conversions between the two, you should be able to 'fight your way' out by using detail::variant_t at the appropriate times.

为在呼叫站点上获得语法方便而必须支付的价格。

This is the price you have to pay for getting syntactic convenience at the call site.

这篇关于如何在包含类型的变量上使用比较运算符?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆