没有定义如何实现升压运算符

时间:2018-10-11 16:58:53

标签: c++ c++11

我正在尝试实现类似于boost / operator的功能。

这是我到目前为止所拥有的:

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;
}

但是我不知道如何在没有#define的情况下实现。

是否有一种无需编写myint(const strong_type &other)即可实现的方法?

2 个答案:

答案 0 :(得分:1)

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_typesome_type += some_type

答案 1 :(得分:0)

Y牛-亚当·内夫罗蒙特绝对非常好,
但是我发现更简单明了的方法。

如果您稍作思考,所有这些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;
}