Проверка SFINAE для оператора + =

Я пытаюсь исключить перегрузку из набора перегрузки, если operator+= отсутствует.

Я знаю, как проверить, является ли T+T законным:

template<typename T,
         typename CheckTplusT = decltype(std::declval<T>() + std::declval<T>())>
void foo(T a, T b, ...)
{
  a = a + b;
}

но это не работает для +=

template<typename T,
         typename CheckTplusT = decltype(std::declval<T>() += std::declval<T>())>
void foo(T a, T b, ...)
{
  a += b;
}

Является ли это фиксируемым, используя другое выражение внутри decltype или мне нужна другая конструкция SFINAE?

Причина, по которой мне нужно это исключить из набора перегрузки, заключается в том, что он сталкивается с другой перегрузкой, которая принимает функтор, который будет использоваться в качестве альтернативы +=. Компиляторы - VS2013, gcc4.8

Ответы

Ответ 1

Я бы записал вторую форму как:

template<typename T>
auto foo(T a, T b, ...) -> decltype( a+=b, void() )
{
  a += b;
}

Выведенный тип для decltype(a+=b, void()) будет просто void, если выражение a+=b допустимо, иначе это приведет к SFINAE.

Ну, даже в первой форме я бы использовал подход типа возвращаемого возврата.

Ответ 2

Вам нужно lvalue в левой части +=, но ваше решение имеет значение xvalue. Как указано в комментариях dyp, вы можете использовать declval<T&> для получения значения lvalue. Это прекрасно работает (просто протестировано):

template<typename T,
         typename CheckTplusT = decltype(std::declval<T&>() += std::declval<T>())>
void foo(T a, T b, ...)
{
}

Ответ 3

Добавление этой функции main():

int main()
{
    int x = 1, y = 2;
    foo( x, y );
}

Это ошибка компилятора:

 main.cpp: In function int main():  main.cpp:15:15: error: no matching
 function for call to foo(int&, int&)
      foo( x, y );
            ^  main.cpp:15:15: note: candidate is:  
 main.cpp:7:6: note: template<class T, class CheckTplusT> void foo(T, T, ...)  void

 foo(T a, T b, ...)
   ^ main.cpp:7:6: note:   template argument deduction/substitution failed: 
    main.cpp:6:60: error:    
      using xvalue (rvalue reference) as lvalue
       typename CheckTplusT = decltype(std::declval<T>() += std::declval<T>())>

Ключевая строка using xvalue (rvalue reference) as lvalue

Это документация для declval

Это обходное решение работает для меня:

template<typename T,
     typename CheckTpluseqT = decltype(*std::declval<T*>() += *std::declval<T*>())>
void foo(T &a, T b, ...)
{
   a += b;
 }

int main()
{
   int a = 1, b = 2;
   foo( a, b );
   std::cout << a << std::endl;
}

выходы 3

Вы также можете использовать declval<T&>, конечно.

Ответ 4

Как насчет этого? это метод, используемый до std::declval.

template<typename T,
         typename CheckTplusT = decltype(*(T*)nullptr += std::declval<T>())>
void foo(T a, T b, ...)
{
  a += b;
  std::cout << "foo with +=" << std::endl;
}