Lambdas sobrecargado en C ++ y diferencias entre clang y gcc

Estoy jugando con un truco para sobrecargar lambdas en C ++. Específicamente:

// For std::function #include  // For std::string #include  // For std::cout #include  template  struct overload : F... { overload(F... f) : F(f)... {} }; template  auto make_overload(F... f) { return overload(f...); } int main() { std::function  f = [](int x,int y) { return x+y; }; std::function  g = [](double x,double y) { return x+y; }; std::function  h = [](std::string x,std::string y) { return x+y; }; auto fgh = make_overload(f,g,h); std::cout << fgh(1,2) << std::endl; std::cout << fgh(1.5,2.5) << std::endl; std::cout << fgh("bob","larry") << std::endl; } 

Ahora, el progtwig anterior comstack y funciona bien en clang:

 $ clang++ -g -std=c++14 test01.cpp -o test01 $ ./test01 3 4 boblarry 

No comstack en gcc:

 $ g++ -g -std=c++14 test01.cpp -o test01 test01.cpp: In function 'int main()': test01.cpp:36:25: error: request for member 'operator()' is ambiguous std::cout << fgh(1,2) << std::endl; ^ In file included from test01.cpp:5:0: /usr/lib/gcc/x86_64-pc-linux-gnu/4.9.2/include/g++-v4/functional:2434:5: note: candidates are: _Res std::function::operator()(_ArgTypes ...) const [with _Res = std::basic_string; _ArgTypes = {std::basic_string<char, std::char_traits, std::allocator >, std::basic_string<char, std::char_traits, std::allocator >}] function:: ^ /usr/lib/gcc/x86_64-pc-linux-gnu/4.9.2/include/g++-v4/functional:2434:5: note: _Res std::function::operator()(_ArgTypes ...) const [with _Res = double; _ArgTypes = {double, double}] /usr/lib/gcc/x86_64-pc-linux-gnu/4.9.2/include/g++-v4/functional:2434:5: note: _Res std::function::operator()(_ArgTypes ...) const [with _Res = int; _ArgTypes = {int, int}] test01.cpp:37:29: error: request for member 'operator()' is ambiguous std::cout << fgh(1.5,2.5) << std::endl; ^ In file included from test01.cpp:5:0: /usr/lib/gcc/x86_64-pc-linux-gnu/4.9.2/include/g++-v4/functional:2434:5: note: candidates are: _Res std::function::operator()(_ArgTypes ...) const [with _Res = std::basic_string; _ArgTypes = {std::basic_string<char, std::char_traits, std::allocator >, std::basic_string<char, std::char_traits, std::allocator >}] function:: ^ /usr/lib/gcc/x86_64-pc-linux-gnu/4.9.2/include/g++-v4/functional:2434:5: note: _Res std::function::operator()(_ArgTypes ...) const [with _Res = double; _ArgTypes = {double, double}] /usr/lib/gcc/x86_64-pc-linux-gnu/4.9.2/include/g++-v4/functional:2434:5: note: _Res std::function::operator()(_ArgTypes ...) const [with _Res = int; _ArgTypes = {int, int}] test01.cpp:38:35: error: request for member 'operator()' is ambiguous std::cout << fgh("bob","larry") << std::endl; ^ In file included from test01.cpp:5:0: /usr/lib/gcc/x86_64-pc-linux-gnu/4.9.2/include/g++-v4/functional:2434:5: note: candidates are: _Res std::function::operator()(_ArgTypes ...) const [with _Res = std::basic_string; _ArgTypes = {std::basic_string<char, std::char_traits, std::allocator >, std::basic_string<char, std::char_traits, std::allocator >}] function:: ^ /usr/lib/gcc/x86_64-pc-linux-gnu/4.9.2/include/g++-v4/functional:2434:5: note: _Res std::function::operator()(_ArgTypes ...) const [with _Res = double; _ArgTypes = {double, double}] /usr/lib/gcc/x86_64-pc-linux-gnu/4.9.2/include/g++-v4/functional:2434:5: note: _Res std::function::operator()(_ArgTypes ...) const [with _Res = int; _ArgTypes = {int, int}] Makefile:2: recipe for target 'all' failed make: *** [all] Error 1 

¿Por qué hay una diferencia? Para el registro, estoy usando gcc 4.9.2 y clang 3.5.0.


Editar 1

Evidentemente, este fragmento de código no comstackba también en VC y ya se había informado . Dicho esto, Sean Middleditch publicó una versión funcional del código sobrecargado:

 template struct overload : F1, overload { using F1::operator(); using overload::operator(); overload(F1 f1, Fs... fs) : F1(f1), overload(fs...) {} }; template struct overload : F1 { using F1::operator(); overload(F1 f1) : F1(f1) {} }; template  auto make_overload(F... f) { return overload(f...); } 

Todavía estoy interesado en entender por qué esta versión del código lambda sobrecargado funciona, pero la original no.

Parece un error Clang para mí.

La regla general es que las funciones miembro del mismo nombre en diferentes clases base no se sobrecargan. Por ejemplo:

 struct Foo { void bar(); }; struct Baz { void bar(int); }; struct Quux : Foo, Baz { }; int main() { Quux().bar(); } // error on both GCC and Clang 

Por alguna razón, Clang no puede diagnosticar esta ambigüedad para operator() .

Una using-declaration eleva los miembros de clase base nombrados al scope de clase derivado, lo que les permite sobrecargar. Por lo tanto:

 struct Quux_2 : Foo, Baz { using Foo::bar; using Baz::bar; }; Quux_2().bar(); // OK. 

En la versión de trabajo del código, las declaraciones de using recursivamente llevan cada statement de operator() en los argumentos de la plantilla dentro del scope de la clase más derivada, lo que les permite sobrecargar.

El código original no debería comstackrse, gcc es correcto aquí. Ver [class.member.lookup]:

De lo contrario (es decir, C no contiene una statement de f o el conjunto de declaraciones resultante está vacío), S (f, C) está inicialmente vacío. Si C tiene clases base, calcule el conjunto de búsqueda para f en cada subobjeto de clase de base directa Bi, y fusione cada conjunto de búsqueda S (f, Bi) a su vez en S (f, C).
– [..]
– De lo contrario, si los conjuntos de declaraciones de S (f, Bi) y S (f, C) difieren, la fusión es ambigua …

El conjunto de statement inicial está vacío (la overload no tiene ningún método): combine todas las bases, todas las cuales tienen conjuntos diferentes. Entonces la fusión debería fallar. Esa regla solo se aplica si el conjunto de statement de overload está vacío, por lo que funciona la adición explícita del using F1::operator() .