2

I'm trying to use SFINAE in c++11 to select between two functions with the same signature, but different body based on the fact that there should be or not a typedef inside the given T type. For example, I have these two structs, one with MyTypedef defined and one that has nothing:

struct MyStruct1
{
    typedef std::false_type MyTypedef;

    ...
}

struct MyStruct3
{
    ...
}

Then I have two template functions that should take the type of the struct as T and do different things based on the existence of MyTypedef. In particular, if T::MyTypedef is defined I should call the first function, in all other cases I should call the second function. I tried something like this:

template<typename T>
void myFunction()
{
    ...
}

template<typename T, typename std::enable_if<T::MyTypedef>::type* = nullptr>
typename myFunction()
{
    ...
}

But it doesn't compile, error is C2668 (MSVC) - 'function' : ambiguous call to overloaded function

T.C.
  • 133,968
  • 17
  • 288
  • 421
Stefano
  • 3,213
  • 9
  • 60
  • 101
  • 2
    C++0x no longer exists, it was a draft name for C++11 – MSalters Feb 13 '16 at 23:48
  • You also have to disable the first overload, when the second is enabled an vice versa – MikeMB Feb 13 '16 at 23:49
  • I've simplified the example code by removing a case unneeded for the question. Could you explain how to disable the first overload and enable the second only if T::MyTypedef is defined? – Stefano Feb 13 '16 at 23:50
  • Also, `typedef` is a poor, old-fashioned way to define aliases, I suggest you to start using `using` if possible. – skypjack Feb 14 '16 at 00:13
  • 1
    @Stefano: If you want to employ SFINAE on overload sets like this, then there must never be a case where a template parameter can cause both overloads to be visible. In your case, your first overload must have the reverse of your SFINAE test. I have no idea how to go about doing such a test, or I would have posted an answer. – Nicol Bolas Feb 14 '16 at 00:52

2 Answers2

5

You can write a trait to detect if the typedef exists, using, e.g., void_t, and SFINAE based on that:

template<class...> struct voider { using type = void; };
template<class... Ts> using void_t = typename voider<Ts...>::type;

template<class T, class = void>
struct has_MyTypedef : std::false_type {};

template<class T>
struct has_MyTypedef<T, void_t<typename T::MyTypedef>> : std::true_type {};

template<typename T, std::enable_if_t<has_MyTypedef<T>{}, int> = 0>
void myFunction()
{
    ...
}

template<typename T, std::enable_if_t<!has_MyTypedef<T>{}, int> = 0>
void myFunction()
{
    ...
}

Or write internal helpers so that you can disambiguate between them in the both-viable case using a dummy parameter:

template<class T, class = typename T::MyTypedef>
void myFunctionImpl(int) {
    // ...
}
template<class T>
void myFunctionImpl(...) {
    // ...
}

template<class T>
void myFunction(){
    return myFunctionImpl<T>(0);
}
T.C.
  • 133,968
  • 17
  • 288
  • 421
  • Your first code does exactly what I wanted to do, however it did not compile. I had to change template{}, int> = 0> to template::value, int>::type = 0> to successfully use it. – Stefano Feb 14 '16 at 11:54
  • 1
    Could you please post an explanation of how IT works? – Stefano Feb 15 '16 at 16:35
1

Your code fails to compile because in the "has typedef" case, both overloads exist and are valid. The compiler cannot pick between them. There are 3 basic approaches.

First, simply invert the condition. You write "has typedef" and "does not have typedef" in a sfinae friendly way, and enable one function if has, and the other if has not.

I find this is often ugly and scales poorly.

A second approach is to make it so when both are valid, the one you want is preferred, by using overload resolution order tricks (inheritance, varargs, conversion, whatever). This scales slightly better, but I find it hacky and a bit brittle.

A third approach is tag dispatching. This does not support "there is no valid overload" well, but is otherwise clean.

You write a base function. This function does some type-computations and builds some tag types with the result. You then call your implementation functions with the tags, and use that to dispatch.

Imagine you have the trait has_foo<T>. It is type that inherits from std::true_type if the type T has the property foo, and false_type otherwise.

Then we can:

 void bar_impl(std::true_type);
 void bar_impl(std::false_type);

template<class T>
void bar(T  const&){
  bar_impl( has_foo<T>{} );
}

and the bar_impl corresponding to if T has the property foo is called.

Writing such a test is easy. Here is a little library:

 namespace details{
   template<template<class...>class Z, class, class...>
   struct can_apply:std::false_type{};
   template<template<class...>class Z, class...Ts>
   struct can_apply<Z, decltype(void(std::declval<Z<Ts...>>())), Ts...>:
     std::true_type
   {};
 }
 template<template<class...>class Z,class...Ts>
 using can_apply=typename details::can_apply<Z,void,Ts...>::type;

Then we just write our test:

template<class T>
using bob_subtype = typename T::bob;

Is the type T::bob. We can test if something has a bob:

template<class T>
using has_bob=can_apply<bob_subtype,T>;

And done.

Yakk - Adam Nevraumont
  • 262,606
  • 27
  • 330
  • 524
  • Although I like your solution too, I find it more complicated than T.C.'s one and I don't really want to have an internal method with a fake argument. Also, in my case SFINAE with two conditions is simpler because I know I will never have more than 2 cases to handle. If it was different, your tag dispatching approach would have probably been better – Stefano Feb 14 '16 at 13:04