Function template parameter pack not at the end of the parameter list

心已入冬 提交于 2019-12-14 00:45:50

问题


The following code compiles and runs ok.

void foo() {

}

template <typename T, typename... Args>
void foo(T x, Args... args) {
  cout << x << endl;
  foo(args...);
}

// inside main()
foo(1,1,1);

This other code does not compile:

void foo() {

}

template <typename... Args, typename T>
void foo(Args... args, T x) {
  foo(args...);
  cout << x << endl;
}

// inside main()
foo(1,1,1);

The compiler says that there is no matching function for call to foo(1,1,1) and says that foo(Args... args, T x) is a candidate, but template argument deduction/substitution failed, because candidate expects 1 argument, but 3 were provided.

Is there any ambiguity with this situation that no compiler can handle? This compile error just seems illogical to me. Maybe this is not in accordance, on purpose, with the C++ standard?


回答1:


The interesting part from Clang's error message is:

main.cpp:11:6: note: candidate template ignored: couldn't infer template argument 'T'

void foo(Args... args, T x) {
     ^

The problem is that the parameter pack Args... occurs prior to T.

Args... is "greedy" and so no parameters are left for the compiler to deduce T, hence it fails.

Quoting the standard (emphasis mine):

[temp.param]/11

A template parameter pack of a function template shall not be followed by another template parameter unless that template parameter can be deduced from the parameter-type-list of the function template or has a default argument. [Example:

...
// U can be neither deduced from the parameter-type-list nor specified
template<class... T, class... U> void f() { } // error
template<class... T, class U> void g() { } // error

— end example]




回答2:


(This answer is based on @JohannesSchaub-litb's comments)

According to the standard, template parameter pack is not deducible if it is used in a function parameter pack not at the end of the parameter list.

§14.8.2.1/1 Deducing template arguments from a function call [temp.deduct.call]:

When a function parameter pack appears in a non-deduced context ([temp.deduct.type]), the type of that parameter pack is never deduced. [ Example:

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

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

— end example ]

And about non-deduced context, §14.8.2.5/5 Deducing template arguments from a type [temp.deduct.type]:

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

So the direct reason of foo(1,1,1); failed is that the template parameter Args is not deduced, which is necessary to make the function invocation valid.

To explain the error message, a template parameter pack not deduced will be deduced to an empty sequence of template arguments[1], it means it'll be omitted. Then foo(1,1,1); failed because the number of arguments doesn't match, that's what compiler complained.

Just as the example from standard shown, you could specify the template argument explicitly to avoid type deduction, even though it doesn't meet the original intent of your code. Such as:

template <typename T, typename... Args>
void foo(Args... args, T x) {
}

int main() {
    // inside main()
    foo<int, int, int>(1, 1, 1);
}

Here're some additional informations.


[1] I can't find direct expression about this in the standard. The most close one is this, "A trailing template parameter pack ([temp.variadic]) not otherwise deduced will be deduced to an empty sequence of template arguments."



来源:https://stackoverflow.com/questions/38629601/function-template-parameter-pack-not-at-the-end-of-the-parameter-list

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!