Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

SFINAE check for operator+=

Tags:

c++

sfinae

I'm trying to eliminate an overload from an overload set if operator+= is missing.

I know how to check if T+T is legal :

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

but this doesn't work for +=

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

Is this fixable by using another expression inside decltype or do I need another SFINAE construct?

The reason I need this eliminated from the overload set is that it clashes with another overload that accepts a functor to be used as an alternative to +=. Compilers are VS2013, gcc4.8

like image 427
MSalters Avatar asked Oct 01 '14 10:10

MSalters


4 Answers

I would write the second form as:

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

The deduced type for decltype(a+=b, void()) would be just void if the expression a+=b is valid, else it would result in SFINAE.

Well, even in the first form, I would use the trailing-return type approach.

like image 80
Nawaz Avatar answered Oct 20 '22 00:10

Nawaz


You need an lvalue on the left hand side of += but your solution has an xvalue. As dyp has stated in the comments, you can use declval<T&> to get an lvalue. This works fine (just tested it):

template<typename T,
         typename CheckTplusT = decltype(std::declval<T&>() += std::declval<T>())>
void foo(T a, T b, ...)
{
}
like image 27
gexicide Avatar answered Oct 20 '22 00:10

gexicide


How about this? it's the method used before 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;
}
like image 1
ikh Avatar answered Oct 20 '22 01:10

ikh


Adding this main() function:

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

This is what the compiler error is:

 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>())>

The key line is using xvalue (rvalue reference) as lvalue

This is the documentation for declval

This workaround works for me:

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

outputs 3

You can also use declval<T&> of course.

like image 1
CashCow Avatar answered Oct 20 '22 00:10

CashCow