Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Not deduced context in variadic function template

The following code should be, as I know, "not deduced context" (or not?)

template <class... X, class Y>
void f(X... args, Y y)
{

}

int main()
{
    f(12);
    f<int, int, int>(1, 2, 3, 4);
}

but g++ 4.9 compiles it for both instantiations of f in main... Can anybody explain?

like image 751
GSi Avatar asked Nov 10 '22 13:11

GSi


1 Answers

The first call f(12) is ill-formed. A parameter-pack that does not appear at the end of a parameter-declaration is a non-deduced context per [temp.deduct.type]/p5.7:

The non-deduced contexts are:

— [..]

A function parameter pack that does not occur at the end of the parameter-declaration-list

Further in [temp.deduct.call]/p1:

For a function parameter pack that occurs at the end of the parameter-declaration-list, the type A of each remaining argument of the call is compared with the type P of the declarator-id of the function parameter pack. Each comparison deduces template arguments for subsequent positions in the template parameter packs expanded by the function parameter pack. When a function parameter pack appears in a non-deduced context (14.8.2.5), the type of that parameter pack is never deduced.

[ Example:

template<class ... Types> void f(Types& ...);
template<class T1, class ... Types> void g(T1, Types ...);
template<class T1, class ... Types> void g1(Types ..., T1);

void h(int x, float& y) {
    const int z = x;
    f(x, y, z); // Types is deduced to int, float, const int
    g(x, y, z); // T1 is deduced to int; Types is deduced to float, int
    g1(x, y, z); // error: Types is not deduced
    g1<int, int, int>(x, y, z); // OK, no deduction occurs
}

— end example ]

Thus the parameter pack X... cannot be deduced by the function's arguments and template argument deduction fails. GCC accepts the first call instead of rejecting the template for not deducing 12 so it appears to be a bug.

The second call, f<int, int, int>(1, 2, 3, 4), however, is well-formed according to [temp.deduct]/p6. The explicitly-specified template arguments are immediately substituted for the template parameters of the function template. This means X = {int, int, int}. Template argument deduction then proceeds with Y being deduced from the rightmost argument as int:

At certain points in the template argument deduction process it is necessary to take a function type that makes use of template parameters and replace those template parameters with the corresponding template arguments. This is done at the beginning of template argument deduction when any explicitly specified template arguments are substituted into the function type, and again at the end of template argument deduction when any template arguments that were deduced or obtained from default arguments are substituted.

Note that also ([temp.deduct]/p2):

There must not be more arguments than there are parameters unless at least one parameter is a template parameter pack, and there shall be an argument for each non-pack parameter.

Clang does not accept the last function call, but GCC does. I believe this to be a Clang bug.


Note that there is an open CWG issue 1609 pertaining to the use of default-arguments following the occurrence of a parameter-pack. There's also LLVM Bug 21774 which disputes Clang's behavior in that context.

like image 118
David G Avatar answered Nov 15 '22 04:11

David G