1

Consider the following example:

#include <iostream>
#include <vector>

template<typename T>
void foo( T && )
{
    std::cout << "Common foo!\n";
}

template<typename T>
void foo( const std::vector<T> & )
{
    std::cout << "Specialized foo!\n";
}

int main()
{
    foo( 5 );
    foo( std::vector { 5 } );
}

As far as I know, when an overload resolution of function templates takes place, the most specialized overload instance is preferred. Cppreference states:

Informally "A is more specialized than B" means "A accepts a subset of the types that B accepts".

To me it is pretty obvious that void foo( const std::vector<T> & ) is more specialized than void foo( T && ). However, the latest Clang and GCC does not seem to agree with me as I get the following output:

Common foo!
Common foo!

This gets fixed by either taking std::vector<T> by value or passing a constant lvalue reference explicitly without making the compiler bind a temporary to it. But why is it the case? Are reference categories considered to be of a higher priority when it comes to partial ordering?

2
  • 7
    foo(T &&) is an exact match. void foo( const std::vector<T> & ) requires to add const. Commented Aug 7 at 17:26
  • You could constrain a forwarding reference in the specialization to get around the problem. example Commented Aug 7 at 17:56

1 Answer 1

5

The candidate resolution here has nothing to do with templates specialization overloading. You have two primary templates, and no specialization is considered in this case (Also there is no such thing as partial function template specialization, you can only specialize it completely or overload with another primary template). After substitution, you have two candidate functions correspondingly:

void foo(std::vector<int>&&)

void foo(const std::vector<int>&)

The first candidate is the most viable function thanks to over.match.best/2.1:

for some argument j, ICSj(F1) is a better conversion sequence than ICSj(F2)

ICSj here stands for "implicit conversion sequence of argument j". In both cases you deal with direct reference binding, however the expression std::vector { 5 } is an rvalue, and as per over.ics.rank/3.2.3 the rvalue reference candidate is better (emphasis mine):

Standard conversion sequence S1 is a better conversion sequence than standard conversion sequence S2 if:

...

S1 and S2 include reference bindings ([dcl.init.ref]) and neither refers to an implicit object parameter of a non-static member function declared without a ref-qualifier, and S1 binds an rvalue reference to an rvalue and S2 binds an lvalue reference

Sign up to request clarification or add additional context in comments.

2 Comments

@wohlstad for a universal reference T&& the "normal" deduction rules apply when dealing with rvalues. lvalue reference deduction (and reference collapsing) happen only if you pass an lvalue to the function
Yes - sorry, I missed the fact that an rvalue is passed.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.