Search code examples
c++templatesgeneric-programming

Template accepts const but not literal


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)


Solution

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