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) { }
Вопрос
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?
Решение
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).