Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Can I disambiguate between overloaded template functions towards more constrained one?

Tags:

c++

c++11

The Code

#include <iostream>
#include <type_traits>

template<typename T,
         typename = typename std::enable_if<std::is_pod<T>::value>::type
         >
T foo(T t) { return t; }

template<typename T>
void foo(T t) { }

int main()
{
    foo<int>(5);
}

The Error

main.cpp: In function 'int main()':
main.cpp:14:15: error: call of overloaded 'foo(int)' is ambiguous
     foo<int>(5);
               ^
main.cpp:14:15: note: candidates are:
main.cpp:7:3: note: T foo(T) [with T = int; <template-parameter-1-2> = void]
 T foo(T t) { return t; }
   ^
main.cpp:10:6: note: void foo(T) [with T = int]
 void foo(T t) { }
      ^

The question

Is it possible to solve The Error in The Code?

like image 410
Bartek Banachewicz Avatar asked Aug 28 '14 11:08

Bartek Banachewicz


3 Answers

Constraining functions with SFINAE does not introduce an ordering, it just means a constrained function is callable or not callable, so when the first overload is enabled you have two ambiguous functions.

The simplest solution is to use the inverse of the same constraint to disable the second overload, so that only one is viable for a given type. To make that work you will need to move the enable_if to the return type, rather than a default template argument (because default template arguments are not part of a function's signature and so can't be used for overloading, only constraining with SFINAE). This solution won't work for lots of overloads unless you can come up with a separate predicate (i.e. combination of type traits) for each overload such that none of the argument types matches more than one predicate. This means the predicates must be mutually exclusive, you cannot have a predicate testing a concept such as signed integral type that is a more refined version of another one testing a concept such as integral type, because both predicates would be true for int and more than one function would be enabled.

One important reason that C++ Concepts are a better solution than SFINAE is that functions constrained by concepts are ordered, so that a more constrained function is a better match than a less constrained one. That means you don't need to play these SFINAE games and have mutually exclusive constraints, the compiler will Do The Right Thing.

like image 97
Jonathan Wakely Avatar answered Oct 19 '22 04:10

Jonathan Wakely


Apply the same SFINAE to the alternate method, just the inverse of it (and move it to the return type); i.e. to disable the other one.

template<typename T>
typename std::enable_if<std::is_pod<T>::value, T>::type
foo(T t) { return t; }

template<typename T>
typename std::enable_if<!std::is_pod<T>::value, void>::type
foo(T t) { }

You mention there will be many overloads, so this may be cumbersome... your mileage may vary.

Note, why only 2? The original question was on 2, but the problem here is more the ordering between many overloads involving template constraints. This answer is left as a description of a more classic solution when there are only a few overloads (with more manageable SFINAE constraints).

like image 6
Niall Avatar answered Oct 19 '22 04:10

Niall


The simplest solution is to disable the second function when the first is constrained.

More generally, you can use overload ranking to prefer a particular function, which scales better. It's something like this:

template<int N> struct choice : choice<N - 1> {};
template<> struct choice<0> {};

Here, the compiler has to perform N derived-to-base conversions to get from N to 0. So we can rank overloads from N to 0, where N is the most desirable and 0 the least desirable.

template<typename T,
         typename = typename std::enable_if<std::is_pod<T>::value>::type
         >
T foo(T t, choice<1>) { return t; }

template<typename T>
void foo(T t, choice<0>) { }

int main()
{
    foo<int>(5, choice<1>());
}

Now you don't have to perform O(n^2) condition duplication.

like image 4
Puppy Avatar answered Oct 19 '22 03:10

Puppy