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)
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.