stanleyerror
stanleyerror

Reputation: 788

C++ overload resolution between template and non-template functions

I am confused about c++ overload resolution between template and non-template functions, following is the sample:

class Stream {};

struct M
{
    M(float v) {}
};

template <class T>
Stream& operator<<(Stream& stream, T& v) {}

Stream& operator<<(Stream& stream, const M& v) {}

int main()
{
    Stream stream;
    int a = 1;

    stream << a; // sample 1
    stream << a * a; // sample 2

    return;
}

Here, sample 1 calls the template function. Sample 2 provides an int&& type parameter, which can implicitly cast to const M&, calls the non-template one, rather then the template one with T = const int.

What happen in overload resolution with sample 2 ?

Upvotes: 3

Views: 808

Answers (3)

NathanOliver
NathanOliver

Reputation: 181057

This really doesn't have anything to do with templates. In

stream << a * a;

a * a materializes an rvalue. Since your template function takes a T& it cannot bind to the temporary so it is discarded as a viable overload.

That leaves you with a user defined conversion to a M and

Stream& operator<<(Stream& stream, const M& v)

as the only viable overload.


If you change your template to use a forwarding reference like

template <class T>
Stream& operator<<(Stream& stream, T&& v) 

then that will be called in both cases as you will get an exact match. Do note that you should constrain T using SFINAE otherwise this overload will be a match for piratically everything.

Additionally you could use

template <class T>
Stream& operator<<(Stream& stream, const T& v)

Upvotes: 5

SergeyA
SergeyA

Reputation: 62613

When used with an lvalue, template is a better match, because it doesn't require calling conversion from int to M.

However, since non-const lvalue references can't bind to rvalues (which is yield by multiplication) this overload can't be used, so the compiler falls back to the conversion.

Upvotes: 2

songyuanyao
songyuanyao

Reputation: 173044

The template one takes parameter by T&, i.e. lvalue-reference to non-const. a * a is an rvalue, which can't be bound to lvalue-reference to non-const (BTW it could be bound to lvalue-reference to const or rvalue-reference). Then the template one won't be considered.

Upvotes: 2

Related Questions