16

Consider the following code:

template <typename T>
struct dependent_type
{
    using type = T;
};

template <typename T>
auto foo(T) -> std::enable_if_t<std::is_same<T, int>{}>
{
    std::cout << "a\n"; 
}

template<typename T> 
void foo(typename dependent_type<T>::type) 
{
    std::cout << "b\n";
}
  • The first overload of foo can deduce T from its invocation.

  • The second overload of foo is a non-deduced context.

int main()
{    
    foo<int>( 1 );      // prints "b"
    foo<double>( 1.0 ); // prints "b"
    foo( 1 );           // prints "a"
}

Why does foo<int>( 1 ) print "b" and not "a"?

wandbox example

Vittorio Romeo
  • 90,666
  • 33
  • 258
  • 416
  • I guess the answer is "b is more specialized, so always called if possible", and the reason would lie somewhere [here](http://en.cppreference.com/w/cpp/language/function_template) (it begins halfway down the page). This is a bit much for me though. – Quentin Jan 13 '17 at 12:46
  • Good question indeed. Thank you. +1 – skypjack Jan 13 '17 at 16:39
  • For the next time, I recommend posting the code as a single snippet, so that people can copy/paste a single time, instead of having to copy/paste two times in order to try your code (and either have the proper include directives, or demonstrate the chosen overload differently). – Johannes Schaub - litb Jan 14 '17 at 23:12
  • 1
    @JohannesSchaub-litb: just click on "wandbox example"? – Vittorio Romeo Jan 14 '17 at 23:26
  • 1
    I had no idea where that link goes to.. I didn't even spot it at first. I guess it must be as confusing as a "My Wallpaper collection" link for users unfamiliar with that "online compiler" concept. – Johannes Schaub - litb Jan 15 '17 at 15:46

1 Answers1

12

Essentially the partial ordering rules say that the dependent_type overload is more specialized because of that non-deduced context.

The process for ordering template functions is based on transforming the template function types and performing template deduction on each in turn, once going from the first template (the one taking T) to the second (the one taking dependent_type), then from the second to the first.

The rules are far too complex to replicate here, but go read [temp.func.order] and the passages it links to if you want the gory details. Here's a quick simplification:

For each template parameter of the template function, make up a unique type and replace the parameter with that. The transformed types for this example are:

void foo(UniqueType); //ignoring the SFINAE for simplicity
void foo(typename dependent_type<UniqueType>::type); 

We then perform template deduction in two directions: once using the parameters of the first template as arguments to the second, and once using the parameters of the second as arguments to the first. This is akin to performing deduction on these function calls:

//performed against template <class T> void foo(typename dependent_type<T>::type);
foo(UniqueType{});                     

//performed against template <class T> void foo(T);        
foo(dependent_type<UniqueType>::type{});

In carrying out these deductions, we're trying to discern whether one overload is more specialized then the other. When we try the first one, deduction fails, since typename dependent_type<T>::type is a non-deduced context. For the second one, deduction succeeds because dependent_type<UniqueType>::type is just UniqueType, so T is deduced to UniqueType.

Since deduction failed going from the second template to the first, the second template is taken as being more specialized than the first. The final result is that the overload resolution prefers the second template for foo<int>(1).

TartanLlama
  • 63,752
  • 13
  • 157
  • 193
  • Could you elaborate a little bit more on what you mean by *"transforming"* and what you're referring to when using the terms *"first template"* and *"second template"*? I understand that the rules are complicated but if I have to be honest your answer does not improve my understanding of this situation at all. – Vittorio Romeo Jan 13 '17 at 13:02
  • 3
    @VittorioRomeo it's explained quite well [here](http://stackoverflow.com/a/17008568/3953764). The compiler can deduce `T` when `dependent_type::type{}` is used as an argument, but it can't deduce `T` from `typename dependent_type::type` when `UniqueType{}` is passed, so the conclusion is that `b` is more specialized – Piotr Skotnicki Jan 13 '17 at 13:32
  • 1
    @VittorioRomeo I tried to explain it a bit more, does that help? – TartanLlama Jan 13 '17 at 13:57
  • @TartanLlama: yes, both your answer and Piotr's link were extremely helpful. I get it now! – Vittorio Romeo Jan 13 '17 at 22:05