Reputation: 3361
When writing a template, class T
may be substituted by a const
type.
Consider:
template<class T> T& min(T& a, T& b) {
return a < b ? a : b;
}
This will work in the following cases:
int a = 1, b = 5;
const int c = 1, d = 5;
min(a, b); // T is int
min(c, d); // T is const int
But will throw a compilation error when called with a literal (like so):
min(1, 5); // T is const int literal
invalid initialization of non-const reference of type ‘int&’ from an rvalue of type ‘int’
Why? Isn't an int literal a const int
? And how can the template be modified to allow working with literals?
(consistent with gcc 6.3 and MSVC 2015)
Upvotes: 1
Views: 179
Reputation: 5720
Isn't an int literal a const int?
No, it is just an int
, not const
, and is defined as a prvalue, hence an lvalue reference cannot bind to it -- as is in your case.
Easily corrected by having the original template like this:
template<typename T>
const T& min(const T& a, const T& b){
return a < b ? a : b;
}
as const T&
will bind to rvalues as well.
Avoid changing to or adding anything the likes of this:
template<typename T, typename U>
auto&& min(T&& a, U&& b){
return std::forward<T>(a < b ? a : b);
}
as here we do not create a copy from the materialized temporary, and as such we're at the risk of returning a dangling reference. See here in [class.temporary]:
A temporary object bound to a reference parameter in a function call ([expr.call]) persists until the completion of the full-expression containing the call.
... at which point it dies. Hence the dangling-ness.
Upvotes: 2
Reputation: 21160
int
literals have type int
, not const int
. T
is therefore deduced to be int
, and int&
can't bind to a prvalue.
The correct way to write such a function is to either perfect forward the arguments, or use const T&
, both of which can bind to anything.
template<typename T, typename U>
auto min(T&& a, U&& b) -> decltype(a < b ? std::forward<T>(a) : std::forward<U>(b))
{
return a < b ? std::forward<T>(a) : std::forward<U>(b);
}
// Or...
template<typename T>
const T& min(const T& a, const T& b)
{
return a < b ? a : b;
}
In the case of perfectly forwarding the arguments, the two template parameters are needed in order for int a{}; min(a, 42);
to compile, as their deduced types are different.
Upvotes: 3
Reputation: 13708
Literal produces a prvalue expression which T&
can not accept. T&
accepts only lvalues.
You can think about it this way: integral literal is a "non-living" thing as it has no address anywhere, how could you bind it to the lvalue reference and then modify it? Where that object would be located? Where the changes would be written?
Upvotes: 0