yohhoyの日記

技術的メモをしていきたい日記

Generic Lambda×Variadic Template Parameter×Ellipsis

C++14で導入されるジェネリックラムダ(generic lambda)、可変長引数テンプレート(variadic template parameter)、ellipsis(...)の組み合わせによる文法上の曖昧さについて。gccとClangのバグ。

struct {
  template<typename...Args>
  auto operator()(Args&&...) const { }
} f1;

auto f2 = [](auto&&...) { };

int main()
{
  f1();
  f2();  // ???
}

gcc 4.9.1(-std=c++1y), Clang 3.5.0(-std=c++14)では、ジェネリックラムダ式(auto&&...)(auto&&, ...)と誤って解釈し(→id:yohhoy:20130407)、それぞれ下記の通りコンパイルエラーとなる(エラーメッセージ抜粋)。*1

error: no match for call to '() ()'
note: candidate is:
note: template
note: template argument deduction/substitution failed:
note: candidate expects 1 argument, 0 provided

error: no matching function for call to object of type '(lambda at source.cpp:6:11)'
note: candidate function template not viable: requires at least 1 argument, but 0 were provided

なお、ジェネリックラムダ式で関数パラメータパック(function parameter pack)の名前(下記コードではargs)を明示すれば、gcc, Clangともに期待通りコンパイルされる。

auto f3 = [](auto&&... args) { };
f3();  // OK

N3937(C++14 DIS) 8.3.5/15より引用(下線部は強調)。

15 There is a syntactic ambiguity when an ellipsis occurs at the end of a parameter-declaration-clause without a preceding comma. In this case, the ellipsis is parsed as part of the abstract-declarator if the type of the parameter either names a template parameter pack that has not been expanded or contains auto; otherwise, it is parsed as part of the parameter-declaration-clause.102
脚注102) One can explicitly disambiguate the parse either by introducing a comma (so the ellipsis will be parsed as part of the parameter-declaration-clause) or by introducing a name for the parameter (so the ellipsis will be parsed as part of the declarator-id).

ノート:関数/ラムダ式の仮引数宣言周りの定義は分かりづらいが、ellipsisをparameter-declaration-clauseの一部として解釈=Cスタイルの可変引数リスト、ellipsisをdeclarator-idの一部として解釈=C++可変長引数テンプレートの関数パラメータパック となる。(C++11 8.3.5/p13)

関連URL

*1:1個の任意型+Cスタイルの可変引数リストを引数に取るジェネリックラムダ式と誤解釈している。このため、実引数0個での呼び出し f2() に適合する候補が無いというコンパイルエラーになっている。