Search code examples
c++templatestype-deduction

Why do templates not implicitly instantiate to const or reference types?


Consider the following function template with explicit specializations.

template<typename T>
void f(T);

template<>
void f<int>(int i) { std::cout << "f() chose int\n"; ++i; }

template<>
void f<const int&>(const int&) { std::cout << "f() chose const int&\n"; }

The first specialization can be implicitly instantiated. The second cannot, even if the first specialization is absent. This is unlike the rules for function overloading where a function taking int or const int& works fine (g() in linked examples).

Example with a specialization for int available. Works.

http://coliru.stacked-crooked.com/a/1680748749f36631

Example with only a specialization for const int& available. Compiles but fails to link.

http://coliru.stacked-crooked.com/a/ab8b068d3f807837

Why does template type deduction work this way and why was it chosen to work this way? The alternative would be for template type deduction to behave like function overloading.

My understanding is that with overloaded functions, the compiler already knows all the available options but with templates the compiler must first decide what to look for and then see if it can be instantiated. If this is the case, then would requiring the compiler to search for qualified variations on types be an unreasonable demand?


Solution

  • Consider the case of no explicit specializations:

    template <typename T>
    void f(T x)
    {
        T y = 42 + x;
        std::cout << y;
    }
    
    int main()
    {
        int n = 1337;
        f(n);
    }
    

    Template argument deduction never deduces a reference type, because the alternative would be to always deduce a reference type. If it did, the above call to f(n) would call f<int&>, which would make T y = 42 + x; ill-formed.

    Picking a specialization happens after deduction is complete.

    The first specialization can be implicitly instantiated. The second cannot, even if the first specialization is absent.

    You can't make f<int> absent, you declared it here:

    template<typename T>
    void f(T);