C ++中的重载lambdas和clang和gcc之间的区别 [英] Overloaded lambdas in C++ and differences between clang and gcc

查看:136
本文介绍了C ++中的重载lambdas和clang和gcc之间的区别的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我正在玩一个技巧来重载C ++中的lambdas。具体来说:

  // for std :: function 
#include< functional&

//对于std :: string
#include< string>

//对于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;
}

现在,上面的程序在clang中编译和工作:

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

它不能在gcc中编译:

  $ g ++ -g -std = c ++ 14 test01.cpp -o test01 
test01.cpp:在函数'int main ()':
test01.cpp:36:25:error:对成员'operator()'的请求是不明确的
std :: cout< fgh(1,2) std :: endl;
^
在test01.cpp中包含的文件中:5:0:
/usr/lib/gcc/x86_64-pc-linux-gnu/4.9.2/include/g++-v4/功能:2434:5:注意:候选是:_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:对成员'operator()'的请求是不明确的
std :: cout< fgh(1.5,2.5) std :: endl;
^
在test01.cpp中包含的文件中:5:0:
/usr/lib/gcc/x86_64-pc-linux-gnu/4.9.2/include/g++-v4/功能:2434:5:注意:候选是:_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:member'operator()'的请求是不明确的
std :: cout< fgh(bob,larry)<< std :: endl;
^
在test01.cpp中包含的文件中:5:0:
/usr/lib/gcc/x86_64-pc-linux-gnu/4.9.2/include/g++-v4/功能:2434:5:注意:候选是:_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:目标'all'失败的配方
make:*** [all]错误1
pre>

为什么有区别?对于记录,我使用gcc 4.9.2和clang 3.5.0。






编辑1



显然,这段代码在VC上编译失败,并且已经报告。也就是说,Sean Middleditch发布了一个工作版本的重载代码:

  template< class F1 ,class ... Fs> 
struct overload:F1,overload< Fs ...>
{
using F1 :: operator();
using overload< Fs ...> :: operator();
过载(F1 f1,Fs ... fs):F1(f1),overload< Fs ...>(fs ...){}
}

模板< 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 ...);
}


$ b $ p我还是有兴趣了解为什么这个版本的重载的lambda代码

解决方案

看起来像是一个Clang bug给我。



一般规则是不同基类中相同名称的成员函数不会重载。例如:

  struct Foo {void bar(); }; 
struct Baz {void bar(int); };
struct Quux:Foo,Baz {};

int main(){Quux()。bar(); } //在GCC和Clang上的错误

无论什么原因,Clang无法诊断 operator()



A 命名的基类成员到派生类范围,允许它们重载。因此:

  struct Quux_2:Foo,Baz {using Foo :: bar;使用Baz :: bar; }; 
Quux_2()。bar(); // 好。

在工作版本的代码中,使用声明递归地将模板参数中的每个运算符()声明带入最大派生类的范围,允许它们重载。


I'm playing with a trick to overload lambdas in C++. Specifically:

// 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;
}

Now, the above program compiles and works fine in clang:

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

It does not compile in 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

Why is there a difference? For the record, I'm using gcc 4.9.2 and clang 3.5.0.


Edit 1

Evidently, this snippet of code failed to compile on VC as well and had already been reported. That being said, Sean Middleditch posted a working version of the overloaded code:

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...);
}

I'm still interested in understanding why this version of the overloaded lambda code works, but the original one does not.

解决方案

Looks like a Clang bug to me.

The general rule is that member functions of the same name in different base classes do not overload. For example:

struct Foo { void bar(); };
struct Baz { void bar(int); };
struct Quux : Foo, Baz { };

int main() { Quux().bar(); } // error on both GCC and Clang

For whatever reason, Clang fails to diagnose this ambiguity for operator().

A using-declaration lifts the named base class members to the derived class scope, allowing them to overload. Hence:

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

In the working version of the code, the using declarations recursively bring every operator() declaration in the template arguments into the scope of the most derived class, allowing them to overload.

这篇关于C ++中的重载lambdas和clang和gcc之间的区别的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆