Nanashi No Gombe
Nanashi No Gombe

Reputation: 620

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?

Upvotes: 0

Views: 235

Answers (3)

Caleth
Caleth

Reputation: 62636

Parameters with defaults are still positional. Your example is the same as

void foo(int i=8, const char * c="hello world")
{
    std::cout << "Default param \t= " << i << "\n";
    std::cout << "Additional param \t= " << c << "\n";
}

int main()
{
    foo();                  // 8 "hello world"
    foo(1, "str1");         // 1 "str1"
    foo("str3");            // ERROR: parameter mismatch

    return 0;
}

Upvotes: 5

NathanOliver
NathanOliver

Reputation: 180500

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.

Upvotes: 5

Guillaume Racicot
Guillaume Racicot

Reputation: 41760

This don't work for the same reasons why this won't work:

void f(int = 0, const char*);

int main() {
    f("test")
}

If you read the error, you'll see that the compiler cannot convert a string literal into a int. And this is true, the first parameter is a int, and you sent it a string literal.

Parameters cannot be rebound to other position, regardless if there's a default argument or not.

Upvotes: 3

Related Questions