代码之家  ›  专栏  ›  技术社区  ›  Nick

如何在没有定义的情况下实现Boost运算符

  •  0
  • Nick  · 技术社区  · 6 年前

    我试图实现类似于Boost/Operators的功能。

    以下是迄今为止我所拥有的:

    template<typename T, typename TAG>
    struct strong_type{
        explicit strong_type(T v) : v(v){}
    
    
    
        T v;
    };
    
    template<typename T, typename TAG>
    struct addition{
        addition() = default;
    
        using N = strong_type<T, TAG>;
    
        friend N operator+(const N &a, const N &b){
            return N{ a.v + b.v };
        }
    };
    
    struct myint_tag{};
    
    struct myint : 
            strong_type<int, myint_tag>, 
            addition<int, myint_tag>{
    
        using strong_type<int, myint_tag>::strong_type;
    
        myint(const strong_type &other) : strong_type(v){}
    };
    
    int main(){
        myint a{ 2 };
        myint b{ 3 };
    
        // result is not myint, but strong_type<int, myint_tag>
        myint c = a + b;
    }
    

    然而,我不明白没有定义如何实现这一点。

    有没有一种方法可以实现这个而不需要写 myint(const strong_type &other) ?

    2 回复  |  直到 6 年前
        1
  •  1
  •   Yakk - Adam Nevraumont    6 年前
    template<class D>
    struct strong_add {
      friend D operator+( D lhs, D const& rhs ) {
        lhs += rhs; return lhs;
      }
      friend D& operator+=( D& lhs, D const& rhs ) {
        lhs.v += rhs.v;
        return lhs;
      }
    };
    
    struct myint : 
        strong_type<int, myint_tag>, 
        strong_add<myint> {
    
      using strong_type<int, myint_tag>::strong_type;
    };
    

    Live example .

    这使用CRTP。 + 以值作为lhs参数,因为如果您有廉价的移动,复制类型会很昂贵,比如 std::string :

    a + b + c + d + e
    

    天真地 const&, const& 另外,我们每 + ,因为我们在操作员的每个返回点创建了一个全新的对象。

    用一个 value, const& 加上,第一 a 被复制。然后我们做 += b ,然后移动结果,然后 += c 然后移动结果,然后 += e 然后移动结果。只有一份复印件。


    如果你愿意,我们可以走得更远。

    首先,我们这样做:

    template<class T>
    class detect_strong_type {
      template<class X, class Tag>
      static std::true_type tester( strong_type<X, Tag>const* );
      static std::false_type tester( void* );
    
    public:
      using type=decltype( tester( (T*)nullptr ) );
    };
    template<class T>
    using is_strong_type = typename detect_strong_type<T>::type;
    
    enum class operators {
      add, subtract, multiply, divide
    };
    template<operators o>
    using op_tag_t = std::integral_constant<operators, o>;
    template<operators o>
    constexpr op_tag_t<o> op_tag{};
    
    auto default_op( op_tag_t<operators::add> ) { return [](auto& lhs, auto const& rhs){ lhs += rhs; }; }
    auto default_op( op_tag_t<operators::subtract> ) { return [](auto& lhs, auto const& rhs){ lhs -= rhs; }; }
    auto default_op( op_tag_t<operators::multiply> ) { return [](auto& lhs, auto const& rhs){ lhs *= rhs; }; }
    auto default_op( op_tag_t<operators::divide> ) { return [](auto& lhs, auto const& rhs){ lhs /= rhs; }; }
    
    template<operators op, class D, class...Skip>
    void do_operator( op_tag_t<op>, D& lhs, D const& rhs, Skip&&... ) {
      default_op( op_tag<op> )( lhs, rhs );
    }
    
    template<class D>
    struct can_add {
      friend D operator+( D lhs, D const& rhs ) {
        lhs += rhs; return lhs;
      }
      friend D& operator+=( D& lhs, D const& rhs ) {
        do_operator( op_tag<operators::add>, lhs, rhs );
        return lhs;
      }
    };
    template<class D>
    struct can_subtract {
      friend D operator-( D lhs, D const& rhs ) {
        lhs -= rhs; return lhs;
      }
      friend D& operator-=( D& lhs, D const& rhs ) {
        do_operator( op_tag<operators::subtract>, lhs, rhs );
        return lhs;
      }
    };
    template<class D>
    struct can_multiply {
      friend D operator*( D lhs, D const& rhs ) {
        lhs *= rhs; return lhs;
      }
      friend D& operator*=( D& lhs, D const& rhs ) {
        do_operator( op_tag<operators::multiply>, lhs, rhs );
        return lhs;
      }
    };
    template<class D>
    struct can_divide {
      friend D operator/( D lhs, D const& rhs ) {
        lhs *= rhs; return lhs;
      }
      friend D& operator/=( D& lhs, D const& rhs ) {
        do_operator( op_tag<operators::divide>, lhs, rhs );
        return lhs;
      }
    };
    template<class D>
    struct can_math:
        can_add<D>, can_multiply<D>, can_subtract<D>, can_divide<D>
    {};
    

    现在我们教 do_operator 关于 strong_type :

    template<operators op, class D,
      std::enable_if_t< is_strong_type<D>{}, bool> =true
    >
    void do_operator( op_tag_t<op>, D& lhs, D const& rhs ) {
      do_operator( op_tag<op>, lhs.v, rhs.v );
    }
    

    这个作品:

    struct myint : 
        strong_type<int, myint_tag>, 
        can_math<myint>
    {
      using strong_type<int, myint_tag>::strong_type;
    };
    
    int main(){
      myint a{ 2 };
      myint b{ 3 };
    
      myint c = a*b + b - a;
    }
    

    Live example

    现在,这对强大的运营商来说有点过分了。它让你做的是:

    struct some_type: can_add<some_type> {
      std::vector<int> values;
      friend void do_operator( op_tag_t<operators::add>, some_type& lhs, some_type const& rhs ) {
        lhs.values.insert( lhs.values.end(), rhs.values.begin(), rhs.values.end() );
      }
    };
    

    现在 some_type + some_type some_type += some_type 执行。

        2
  •  0
  •   Nick    6 年前

    雅克-亚当·内罗蒙 非常好,
    但我找到了更简单、更清晰的方法。

    如果你想一想,所有这些 addition / strong_add 类,需要注入“全局”运算符。

    操作员本身不需要是朋友,但是 friend 使用关键字,因为否则运算符将不会注入“全局”空间。

    也没有人需要这个结构。它只用于注入运算符,因此它可能是空结构。

    template<typename T, typename TAG>
    struct strong_type{
        using type = T;
    
        T v;
    
        explicit constexpr strong_type(const T &v) : v(v) {}
    };
    

    令人惊讶的是:

    template<class V>
    struct arithmetic{
        friend constexpr V operator+ (const V &a, const V &b){ return { a.v +  b.v }; }
        friend constexpr V operator- (const V &a, const V &b){ return { a.v -  b.v }; }
        friend constexpr V operator* (const V &a, const V &b){ return { a.v *  b.v }; }
        friend constexpr V operator/ (const V &a, const V &b){ return { a.v /  b.v }; }
    
        friend V &operator+=(V &a, const V &b){ a.v += b.v; return a; }
        friend V &operator-=(V &a, const V &b){ a.v -= b.v; return a; }
        friend V &operator*=(V &a, const V &b){ a.v *= b.v; return a; }
        friend V &operator/=(V &a, const V &b){ a.v /= b.v; return a; }
    };
    

    最后:

    struct myint_tag{};
    
    struct myint : strong_type<int, myint_tag>,
            arithmetic  <myint>
    {
    
        using strong_type::strong_type;
    };
    
    int main(){
        constexpr myint a{ 2 };
        constexpr myint b{ 3 };
    
        myint x{ 3 };
        myint y{ 5 };
    
        x = x + y * x;
    }