Search code examples
c++variadic-templatesdefault-valuefunction-templatesexplicit-instantiation

Why don't default parameters work alongside a parameter pack?


Because of 8.3.6 ([dcl.fct.default])/4,

In a given function declaration, each parameter subsequent to a parameter with a default argument shall have a default argument supplied in this or a previous declaration or shall be a function parameter pack.

the following should compile:

#include <iostream>

template<typename ...Ts>
void foo(int i=8, Ts... args)
{
    std::cout << "Default parameters \t= " << i << "\n";
    std::cout << "Additional params  \t= " << sizeof...(Ts) << "\n";
}

int main()
{
    foo();                  // calls foo<>(int)
    foo(1, "str1", "str2"); // calls foo<const char*, const char*>(int, const char*, const char*)
    foo("str3");            // ERROR: does not call foo<const char*>(int, const char*)

    return 0;
}

But it does not compile due to foo("str3") which confuses the compiler. It complains that there is no matching function for call to foo(const char*) and that it cannot convert "str3" (type const char*) to type int.

I understand that one can work around this problem by resorting to function overloading or using the named parameter idiom (cf. where to place default value parameter in variable-length function in c++? and default arguments and variadic functions). However, I would like to know if the compiler is just stupid or if there is a genuine reason why the intended behaviour in the code example above is not implemented. In other words, why does the compiler complain even if I explicitly instantiate the function as foo<const char*>(int, const char*)? It's as if the explicit instantiation simply ignores the value of the default parameter. Why?


Solution

  • The standard you are quoting is just saying it is legal to form

    template<typename ...Ts>
    void foo(int i=8, Ts... args)
    

    When you call it though, you still have to pass an int as the first parameter otherwise the function won't be considered during overload resolution. When you do

    foo("str3");
    

    the compiler is going to look for any function foo that takes a const char* or a const char(&)[5] since that is the only parameter. That means your function is complete ignored because it expects and int for the first parameter or no parameters at all.