I have a dozen functions or so that take two parameters: a generic, and a specific type. E.g.:
template <class A, class B>
void foo(A& a, B& b)
{
cout << "generic fallback" << endl;
}
template <class A>
void foo(A& a, int &i)
{
cout << "generic int" << endl;
}
template <class A>
void foo(A& a, string& s)
{
cout << "generic str" << endl;
}
I want to create an overload which will get called whenever A
is an instance of a particular struct[1]. The best I came up with so far was:
struct mine
{
int is_special;
};
template <class A, class B>
auto foo(A& a, B& b) -> decltype(A::is_special, void())
{
cout << "specialized fallback" << endl;
}
The result I want is:
int x;
string y;
float z;
string generic;
mine special;
foo(generic, x); // generic int
foo(generic, y); // generic string
foo(generic, z); // generic fallback
foo(special, x); // specialized fallback
foo(special, y); // specialized fallback
foo(special, z); // specialized fallback
The above code doesn't work, however, because for the specialized cases, there is an ambiguous overload. Is there any easy way to cause those functions to only get created if A::is_special
is not a valid type? Ideally I would annotate each function with something like:
template <class A, class B>
auto foo(A& a, B& b) -> decltype(doesnt_work(A::is_special), void())
// ...
I ask also in the more general case: given any "positive" SFINAE test which results in a function or class getting created as a result of the test, is there any way to negate that test specifically for use in other cases? In essence, the equivalent of if ... else if
with SFINAE.
I did get this case to work, but I had to rename all foo
to foo_imp
, add a long
parameter to the generic ones, an int
parameter to the specialized one, and then define a foo
that called them (ideone code here). This seems less-than-ideal in that it's not as straightforward, although either way I have to modify all the existing foo
s anyway.
[1] Note that I can't use the type's name because it is a nested template and would thus result in a non-deducible context.
You can manually control overload resolution with an extra parameter:
The viable overload with the highest "rank" will be selected by overload resolution.
You can't directly negate an ad hoc SFINAE constraint ("this expression used in the signature must be well-formed"). You'll need to write an actual trait to detect it, and then negate the result. Simplest way to do it is probably by using
std::experimental::is_detected_v
, recently voted into v2 of the library fundamentals TS: