Overload operator only if template argument

Given the template class A with one argument of the template T, is it possible to overload only the operators in A available for type T? For instance:

template <typename T>
class A
{
public:
    #if hasOperator(T, +=)
    T& operator +=(const T &rhs)
    {
        mValue += rhs;
        return mValue;
    }
    #endif

private:
    T mValue;
}


int main()
{
    A<int> a;
    a += 8; //+= will forward to the += for the int

    struct Test {  /*no operators defined*/ };
    A<Test> b; //+= is not implemented since Test does not implement +=
}

I am writing a generic wrapper class that should behave exactly like a template type. Therefore, if T has the operator + =, A will (at compile time) be an overload + =, respectively. Yes, I could go ahead and simply implement each statement in A, but then the compiler will be wrong if T does not have a specific statement. At first I, although template specialization may be the answer, but this will require specialization for each type. Although this can work and print a lot, it is not, because A needs to work with any type (and not just what specializes).

+4
3

SFINAE, operator+ , T operator+

template <typename T>
class A
{
private:
    T mValue;
public:
    template<typename U=T>
    auto operator +=(const U &rhs)
        -> decltype(mValue += rhs)
    {
        mValue += rhs;
        return mValue;
    }
};

+4

3 . .


, , :

template<class...>struct types{using type=types;};
namespace details {
  template<template<class...>class Z, class types, class=void>
  struct can_apply : std::false_type {};
  template<template<class...>class Z, class...Ts>
  struct can_apply<Z,types<Ts...>,std::void_t<Z<Ts...>>> :
    std::true_type
  {};
}
template<template<class...>class Z, class...Ts>
using can_apply = details::can_apply<Z,types<Ts...>>;

+=:

template<class Lhs, class Rhs>
using plus_equal_result = decltype(std::declval<Lhs>()+=std::declval<Rhs>());

template<class Lhs, class Rhs>
using can_plus_equal = can_apply< plus_equal_result, Lhs, Rhs >;
template<class T>
using can_self_plus_equal = can_plus_equal< T&, T const& >;

, , , +=.

template<class A, class T, bool b = can_self_plus_equal<T>{}>
struct A_maybe_plus_equal {};
template<class A, class T>
struct A_maybe_plus_equal<A, T, true> {
  A& self() { return *static_cast<A*>(this); }
  A& operator+=( T && t )
  {
    self().mResult += std::move(t);
    return self();
  }
  template<class U>
  std::enable_if_t<can_plus_equal<T&,U>{},A&> operator+=( U && u )
  {
    self().mResult += std::forward<U>(u);
    return self();
  }
};

+=, true.

template <class T>
class A:
  public A_maybe_plus_equal<A<T>, T>
{
  friend class A_maybe_plus_equal<A<T>, T>;
public:
  // nothing needed
private:
  T mValue;
};

+=, const T& T&& U&& , T& += T const& .

"" , .

, , .


. , {} , .

, , SFINAE :

template <typename T>
class A {
public:
  template<class U>
  auto operator +=(U&&rhs)
  -> decltype( (std::declval<T&>()+=std::declval<U&&>()),void(),A& )
  // or std::enable_if_t<can_plus_equal<T&,U>{},A&>
  {
    mValue += std::forward<U>(rhs);
    return *this;
  }
private:
  T mValue;
};

{}, . , T const& , template.

, undefined, , , , . += , (, UB).

: - , . , , .

, , () , , . , , , , , .

plus_equal # 1. . , № 1 {} {}, . - - , , , .


. .

template <typename T>
class A {
public:
  A& operator +=(const T &rhs) {
    mValue += rhs;
    return *this;
  }
private:
  T mValue;
};

, SFINAE, += , +=, "". vector operator<. "" , , , .

, , . , SFINAE .


, ++ 1z . , , , , std.

+2

. - . :

, T .

But isn't it clearer than a mistake when A<T>not? If you have:

template <typename T>
class A
{
public:
    A& operator +=(const T &rhs)
    {
        mValue += rhs;
        return *this;
    }

    A& operator-=(const T &rhs)
    {
        mValue -= rhs;
        return *this;
    }

    // etc

private:
    T mValue;
};

Then it will just work:

int main() {
    A<int> a;
    a += 8; //+= will forward to the += for the int

    struct Test {
        Test& operator-=(const Test& ) { return *this; }
    };

    A<Test> b;
    b -= Test{};   // totally fine
    b += Test{};   // error: no match for += 
                   // (operand types are 'main()::Test' and 'const main()::Test')
}
+1
source

Source: https://habr.com/ru/post/1584561/


All Articles