0

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?

Nanashi No Gombe
  • 510
  • 1
  • 6
  • 19
  • 1
    The passage you quoted applies only to the declaration, not the function call. So the function declaration should indeed compile (and it does), but the last call does not (and there's no reason it should). Also, you are not performing explicit instantiation anywhere. Finally, adding a parameter pack does not mean you get to ignore the positional requirements of optional arguments... – Max Langhof Jan 10 '20 at 16:26
  • @MaxLanghof Yes, the above code does not have an explicit instantiation. But I have tried the same with the said explicit instantiation. It does not compile. Given the fact that it also would not compile if I put the default parameter to the right to the parameter pack, it seems to me that the concept of a default parameter in a variadic function does not exist at all. Is that it? – Nanashi No Gombe Jan 10 '20 at 16:57
  • Of course it exists. Your first two function calls demonstrate that it works perfectly fine. You can actually put two optional things (the optional parameter and a possibly empty parameter pack) in the same function, but that doesn't free you from constraints regarding the position of parameters. If your function arguments starts with an optional parameter you either have to provide no parameters at all or you have to provide a value for the optional parameter. Whether the subsequent arguments are variadic or optional changes nothing about this. – Max Langhof Jan 10 '20 at 17:00

3 Answers3

5

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;
}
Caleth
  • 52,200
  • 2
  • 44
  • 75
5

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.

NathanOliver
  • 171,901
  • 28
  • 288
  • 402
  • Is the point of the legality of that declaration only to allow calls to ```foo()```? That's all there is to it? Thanks. – Nanashi No Gombe Jan 10 '20 at 16:54
  • 1
    @NanashiNoGombe Pretty much. Without that exemption it would be illegal to even declare `void foo(int i=8, Ts... args)` since all arguments after the first default argument need to be defaulted as well and there is no way to provide a default for a parameter pack. – NathanOliver Jan 10 '20 at 16:56
3

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.

Guillaume Racicot
  • 39,621
  • 9
  • 77
  • 141