Как операторы повышения могут быть реализованы без определений - PullRequest
0 голосов
/ 11 октября 2018

Я пытаюсь реализовать функциональность, аналогичную надстройке / операторам.

Вот что у меня есть до сих пор:

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 голосов
/ 12 октября 2018

Якк - Адам Невраумонт определенно очень хорош,
но я нашел гораздо более простой и понятный способ сделать то же самое.

Если немного подумать, все эти 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;
}
0 голосов
/ 11 октября 2018
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;
};

Пример в реальном времени .

Используется CRTP.+ принимает аргумент lhs по значению, потому что, если у вас есть недорогие для перемещения типы с дорогой копией, такие как std::string:

a + b + c + d + e

с наивным const&, const& plus, мы получимкопируйте каждый +, так как мы создаем новый объект в каждой точке возврата от оператора.

С плюсом 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;
}

Живой пример

Теперь это немного излишне только для сильных операторов,Это позволяет вам:

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 реализованы.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...