Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Why can template function parameters of type T& be binded to const lvalues but not rvalues?

Tags:

c++

templates

Consider:

template <typename T> void f(T&);

const int b = 2;
f(b);      // ok
f(2);      // error, can not bind rvalue to lvalue reference

Why is f(const int) allowed? the logic seems to indicate that if the programmer did not explicitly define the template parameter as const T&, he/she wanted to modify the binded-to variable.

So in this case the question is, why does template instantiation give itself the freedom to instantiate with consts when those were not explicitly required ?

Even if there was a rationale for allowing template instantiation to instantiate with consts, then why, in that case, would be binding to rvalues forbidden? you can bind rvalues to const lvalue references. In that case, the template would be instantiated to f<const int>, and f(2) would be allowed.

I want to know the reasoning behind these decisions, not references to the standard.

like image 913
4nt Avatar asked Nov 13 '17 21:11

4nt


2 Answers

Why is f(const int) allowed?

You might subsitute the template T by const int to transform T& into const int&

Even if there was a rationale for allowing template instantiation to instantiate with consts, then why, in that case, would be binding to rvalues forbidden?

There is no T (in T&) which (exact) matches int&& for deduction.

f<const int>(42) is allowed, but there is no deduction happening.

I want to know the reasoning behind these decisions, not references to the standard.

So why allowing cv substitution in template?
I would say it make generic programming easier.
Else you would have to provide overloads for each combination of const, volatile.

Here, if you want to restrict T to non const, you may use SFINAE with traits:

template <typename T> std::enable_if_t<!std::is_const<T>::value> f(T&);
like image 165
Jarod42 Avatar answered Sep 27 '22 21:09

Jarod42


So in this case the question is, why does template instantiation give itself the freedom to instantiate with consts when those were not explicitly required?

Quite simply nothing in the implementation of f prevents instantiating the template with const int. Try the following instead and you'll observe a problem.

template <typename T> void f(T& v) { v = 3; };
const int b = 2;
f(b);      // error: assignment of read-only reference (within the template instance)
int c = 2;
f<const int>(c); // error

The fact that b is const int means the template is instantiated as <const int> and this is not possible because v is modified. Explicit instantiation as <const int> is similarly disallowed.

Why is it deduced as const int? It should have been deduced as int.

When the type to instantiate the template is not explicitly specified, it is deduced based on the type passed into the function which is const int. Expecting it deduce a different type is impractical (if not outright absurd).

Please also note that if it did deduce the type as int then there would be an error passing const int into the function. As can be demonstrated by explicitly instantiating the template as <int>.

template <typename T> void f(T&) {};
const int b = 2;
f<int>(b); //error: no matching function for call to 'f<int>(const int&)

why, in that case, would be binding to rvalues forbidden?

The compiler would need to implicitly assume an appropriate type to use for instantiating the template and then perform the implicit conversion from rvalue to const lvalue reference. (Seems a a stretch expectation to me.)

Note that if you explicitly instantiate the template as <const int> then binding rvalue to const lvalue reference works because the conversion can be done.

template <typename T> void f(T&);
f<const int>(2);      // OK
like image 23
Disillusioned Avatar answered Sep 27 '22 19:09

Disillusioned