3

I have the following sample code:

class Serializable {};

class MyData : public Serializable {};

void GetData( Serializable& ) {}

template<typename T>
void GetData( T& data )
{
    std::istringstream s{"test"};
    s >> data;
}

int main()
{
    MyData d;
    GetData(d);
}

(Live Sample)

Based on overload resolution rules, the non-template version should be preferred because the base class is of type Serializable. However, I expect SFINAE to kick in when there are errors in the template version when it is instantiated for overload resolution (because if the >> operator is not defined for a type, it should not be considered).

Why is it still failing even though the template won't be used?

Columbo
  • 60,038
  • 8
  • 155
  • 203
void.pointer
  • 24,859
  • 31
  • 132
  • 243
  • `the non-template version should be preferred` only if it's an exact match. See what happens if you do `MyData` instead of `Serializable`. –  Dec 04 '14 at 18:21

1 Answers1

5

Based on overload resolution rules, the non-template version should be preferred because the base class is of type Serializable.

Not quite. [over.match.best]:

Given these definitions, a viable function F1 is defined to be a better function than another viable function F2 if for all arguments i, ICSi(F1) is not a worse conversion sequence than ICSi(F2), and then

  • for some argument j, ICSj(F1) is a better conversion sequence than ICSj(F2), or, if not that,
  • […]
  • F1 is not a function template specialization and F2 is a function template specialization […]

That means that only if the deduced specialization of the function template necessitates a conversion that isn't better than the conversion that the normal function necessitates, your rule applies. And the binding of d to Serializable& is a worse conversion than the binding of d to MyData& (which is the type of the parameter of the specialization), [over.ics.ref]:

When a parameter of reference type binds directly (8.5.3) to an argument expression, the implicit conversion sequence is the identity conversion, unless the argument expression has a type that is a derived class of the parameter type, in which case the implicit conversion sequence is a derived-to-base Conversion (13.3.3.1).

However, I expect SFINAE to kick in when there are errors in the template version when it is instantiated for overload resolution (because if the >> operator is not defined for a type, it should not be considered).

SFINAE doesn't apply for the content of a function template. [temp.deduct]/8:

Only invalid types and expressions in the immediate context of the function type and its template parameter types can result in a deduction failure.

Hence the deduced specialization of the function template is indeed chosen, and causes a compiler error while instantiating its definition.

Columbo
  • 60,038
  • 8
  • 155
  • 203
  • The standards usage of the word "specialization" here is really "instantiation" of the template then, correct? Because specialization in my mind is when a function is explicitly implemented for types. In my case I am not explicitly specializing the base function template. – void.pointer Dec 04 '14 at 19:15
  • 1
    @void.pointer It seems you've mixed up the terminology. The *specialization* is the function that was instantiated with the deduced template arguments. What you're talking about is an *explicit specialization*. – Columbo Dec 04 '14 at 19:20