3

Look at this (simplified) example:

int foo(int) { return 0;}
double foo(double) { return 0.0; }

template <class T>
enable_if<is_integral<T>::value>
bar(T(*f)(T)) {}

int main()
{
   bar(foo);
   return 0;
}

My expectation was that the compiler will first try to instantiate the template for each overload, which would fail (SFINAE) for the second overload and therefore there would be only void bar(int(*f)(int) left in the set of candidates, which would resolve using the first overload of foo. That is not what happens. It fails with something like this:

no matching function for call to ‘bar(<unresolved overloaded function type>)’
couldn't deduce template parameter ‘T’

Is there any way to achieve something like this?

Baruch
  • 20,590
  • 28
  • 126
  • 201
  • How would the compiler be able to infer the template argument T? How would you know which 'foo' is referred when given 'T foo(T)'? – dgrine Dec 31 '17 at 14:56
  • 2
    @OnMyLittleDuck With how the OP thought it worked, it would work: the compiler would try inferring `T` for the `int foo(int)` overload, and would try it for the `double foo(double)` overload. Then, `T = double` would cause a substitution error, so only `T = int` would remain. Now, although C++ doesn't work like that, the OP's guess does make sense. –  Dec 31 '17 at 15:00
  • https://stackoverflow.com/q/27523684/3002139 is pretty close. Highly related at least, probably dupe. – Baum mit Augen Dec 31 '17 at 15:08
  • https://stackoverflow.com/q/30623939/3002139 is also quite close. OP if any of them fit your needs, I'll be happy to hammer. – Baum mit Augen Dec 31 '17 at 15:11
  • I will have to look at them more closely. – Baruch Dec 31 '17 at 15:12
  • Another one: https://stackoverflow.com/q/28938839/3002139 – Baum mit Augen Dec 31 '17 at 15:13

1 Answers1

2

C++'s type deduction is very simplistic. The clauses [temp.deduct.call]/6.2 and [over.over]/1 describe ways in which an overloaded name can be used as an argument.

In your example, both the deductions would succeed (to T=int and T=double) and one substitution would fail. But the language requires only one deduction succeed.

You asked how to achieve it. Here are some options:

  1. Do not use an overloaded name, static_cast to the desired function type, or explicitly provide the type of T in the call to bar.
  2. Alter the overloads such that deduction will succeed for only one of the overloads. In other words, exclude overloads by deduction, not by substitution. See below for an example.
  3. Add another parameter which can deduce T.
  4. Pass the buck and avoid deducing T until a later point.

Example for #2:

tempate<class T> struct integral_wrapper { T t; }

integral_wrapper<int> foo(int);
double foo(double);

template<class T>
void bar(integral_wrapper<T> foo(T));

Example for #3:

template<class T>
void bar(T (*f)(T), T);

bar(foo, 0);

Example for #4:

struct foo_t
{
    int operator()(int);
    double operator()(double);
} foo;

template<class F>
void bar(F);

bar(foo);

Note that a generic lambda can be another approach for #4.

Depending on your usage, some of these may be more attractive than others. Approach #4 is particularly useful when passing arguments to STL algorithms.

Jeff Garrett
  • 5,863
  • 1
  • 13
  • 12