wye*_*r33 26 c++ gcc clang overload-resolution c++11
我正在玩一个用C++重载lambdas的技巧.特别:
// For std::function
#include <functional>
// For std::string
#include <string>
// For std::cout
#include <iostream>
template <class... F>
struct overload : F... {
overload(F... f) : F(f)... {}
};
template <class... F>
auto make_overload(F... f) {
return overload<F...>(f...);
}
int main() {
std::function <int(int,int)> f = [](int x,int y) {
return x+y;
};
std::function <double(double,double)> g = [](double x,double y) {
return x+y;
};
std::function <std::string(std::string,std::string)> 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;
}
Run Code Online (Sandbox Code Playgroud)
现在,上面的程序编译并在clang中正常工作:
$ clang++ -g -std=c++14 test01.cpp -o test01
$ ./test01
3
4
boblarry
Run Code Online (Sandbox Code Playgroud)
它不能在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<_Res(_ArgTypes ...)>::operator()(_ArgTypes ...) const [with _Res = std::basic_string<char>; _ArgTypes = {std::basic_string<char, std::char_traits<char>, std::allocator<char> >, std::basic_string<char, std::char_traits<char>, std::allocator<char> >}]
function<_Res(_ArgTypes...)>::
^
/usr/lib/gcc/x86_64-pc-linux-gnu/4.9.2/include/g++-v4/functional:2434:5: note: _Res std::function<_Res(_ArgTypes ...)>::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<_Res(_ArgTypes ...)>::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<_Res(_ArgTypes ...)>::operator()(_ArgTypes ...) const [with _Res = std::basic_string<char>; _ArgTypes = {std::basic_string<char, std::char_traits<char>, std::allocator<char> >, std::basic_string<char, std::char_traits<char>, std::allocator<char> >}]
function<_Res(_ArgTypes...)>::
^
/usr/lib/gcc/x86_64-pc-linux-gnu/4.9.2/include/g++-v4/functional:2434:5: note: _Res std::function<_Res(_ArgTypes ...)>::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<_Res(_ArgTypes ...)>::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<_Res(_ArgTypes ...)>::operator()(_ArgTypes ...) const [with _Res = std::basic_string<char>; _ArgTypes = {std::basic_string<char, std::char_traits<char>, std::allocator<char> >, std::basic_string<char, std::char_traits<char>, std::allocator<char> >}]
function<_Res(_ArgTypes...)>::
^
/usr/lib/gcc/x86_64-pc-linux-gnu/4.9.2/include/g++-v4/functional:2434:5: note: _Res std::function<_Res(_ArgTypes ...)>::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<_Res(_ArgTypes ...)>::operator()(_ArgTypes ...) const [with _Res = int; _ArgTypes = {int, int}]
Makefile:2: recipe for target 'all' failed
make: *** [all] Error 1
Run Code Online (Sandbox Code Playgroud)
为什么会有区别?为了记录,我使用的是gcc 4.9.2和clang 3.5.0.
显然,这段代码也无法在VC上编译,并且已经报告过.话虽如此,Sean Middleditch发布了重载代码的工作版本:
template<class F1, class... Fs>
struct overload : F1, overload<Fs...>
{
using F1::operator();
using overload<Fs...>::operator();
overload(F1 f1, Fs... fs) : F1(f1), overload<Fs...>(fs...) {}
};
template<class F1>
struct overload<F1> : F1
{
using F1::operator();
overload(F1 f1) : F1(f1) {}
};
template <class... F>
auto make_overload(F... f) {
return overload<F...>(f...);
}
Run Code Online (Sandbox Code Playgroud)
我仍然有兴趣理解为什么这个版本的重载lambda代码有效,但原始代码没有.
T.C*_*.C. 16
对我来说看起来像一个Clang bug.
一般规则是不同基类中同名的成员函数不会重载.例如:
struct Foo { void bar(); };
struct Baz { void bar(int); };
struct Quux : Foo, Baz { };
int main() { Quux().bar(); } // error on both GCC and Clang
Run Code Online (Sandbox Code Playgroud)
无论出于何种原因,Clang未能诊断出这种歧义operator().
甲using-declaration升降机命名基类成员派生类范围,允许它们超载.因此:
struct Quux_2 : Foo, Baz { using Foo::bar; using Baz::bar; };
Quux_2().bar(); // OK.
Run Code Online (Sandbox Code Playgroud)
在代码的工作版本中,using声明递归地将operator()模板参数中的每个声明带入最派生类的范围,允许它们重载.
Bar*_*rry 10
原始代码不应该编译,gcc在这里是正确的.见[class.member.lookup]:
否则(即,C不包含f的声明或结果声明集为空),S(f,C)最初为空.如果C具有基类,则在每个直接基类子对象Bi中计算f的查找集,并将每个这样的查找集S(f,Bi)依次合并为S(f,C).
- [..]
- 否则,如果S(f,Bi)和S(f,C)的声明集不同,则合并是不明确的......
初始声明集是空的(overload没有方法) - 所以合并所有基础,所有基础都有不同的集合.合并应该失败.该规则仅适用于声明集overload为空的情况,这就是明确添加using F1::operator()作品的原因.