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.
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&);
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 asint
.
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
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With