Вопрос

My program generates random strings that have a "type" and an identifier. Like so:

float       lHTY8Au8b9 = float();
int         dO3PNUInH6 = int();
float       B_MtShimak = float();
float       hgi_TzaVEv = float();
double      mfW8kr6h6q = double();
std::string lSLj9antfj = std::string();
char        MQkeARWYTL = char();
char        Oe7G_ZRJy6 = char();
float       qUwmOWeilK = float();
double      FJYIODwQfx = double();

Then I have the following to see if two arbitrary variables can be added:

template <typename A, typename B>
typename std::enable_if<std::is_convertible<A, B>::value, decltype(A() + B())>::type
    try_to_add(A a, B b) {
        return a + b;
    }

Of course, it works because I get the following error:

main.cpp:51:54: error: no matching function for call to ‘try_to_add(double&, std::string&)’

  try_to_add<double,std::string>(mfW8kr6h6q,lSLj9antfj);

However what I want instead is a list of candidates that don't cause a compile error. I can't hardcode the template parameters because it only accepts ints, and I cannot check inside the generator program because they're just strings, and attempting to use try_to_add with non-convertible types will cause a compile error. I know that SFINAE is the wrong approach, but is there a way to get try_to_add to simply do nothing, rather than generate a compile error?

Coliru

Это было полезно?

Решение

Add another overlaod with the inverse condition that does nothing.

template <typename A, typename B>
typename std::enable_if<!std::is_convertible<A, B>::value>::type
try_to_add(A, B) { }

Другие советы

Here is a few useful techniques. First, instead of is_convertible, we just try adding. if it works, bonus. Second, I use perfect forwarding.

Finally, I use a variardic pack for the "does work" catch-all. This is the sink technique.

#include <iostream>

#define RETURNS(X) ->decltype(X) { return (X); }

template<typename A, typename B>
auto try_to_add(A&& a, B&& b)
  RETURNS( std::forward<A>(a) + std::forward<B>(b) )

template<typename... Ts>
void try_to_add( Ts&&... ) {
  std::cout  << "failed to add\n";
}

struct foo {};

int main() {
  int a =1, b=2;
  std::cout << try_to_add( a, b ) << "\n";
  try_to_add( a, foo{} );
}

The RETURNS macro works around the lack of automatic return type deduction in C++11. In C++1y we will have a better option that lets us omit the repeated -> (X) { returns (X); } annoyance.

The point of the variardic args is that such a pack will only match if all overloads without the variardic args fail to match (they are considered last). This is useful because you don't have to maintain the bool condition in two different spots at once (once inverted).

The RETURNS macro can be expanded:

template<typename A, typename B>
auto try_to_add(A&& a, B&& b)
-> decltype( std::forward<A>(a) + std::forward<B>(b) )
  { return ( std::forward<A>(a) + std::forward<B>(b) ); }

if you prefer not to use macros (which is understandable).

Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top