C ++ 11“过载lambda”与可变模板和变量捕获 [英] C++11 "overloaded lambda" with variadic template and variable capture

查看:419
本文介绍了C ++ 11“过载lambda”与可变模板和变量捕获的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我正在调查可能被称为重载lambda的C ++ 11成语:





重载 功能可变参数模板似乎非常吸引我,但结果证明它不适用于变量捕获:任何 [&] [=] [y] [& y] ] etc if在成员函数中)导致编译失败:错误:没有匹配的调用'(overload< main(int,char **)::& (int)>,main(int,char **)::< lambda(char *)>)(char *&)'和ideone.com GCC 5.1)



另一方面,固定的2-ary案件没有遇到这个问题。 (尝试将第一个 #if 0 更改为ideone.com上的 #if 1



对这里发生了什么的想法?这是一个编译器错误,还是我偏离了C ++ 11/14规范?



http://ideone.com/dnPqBF

  #include< iostream> 
using namespace std;

#if 0
template< class F1,class F2>
struct overload:F1,F2 {
overload(F1 f1,F2 f2):F1(f1),F2(f2){}

;
using F2 :: operator();
};

template< class F1,class F2>
auto make_overload(F1 f1,F2 f2){
return overload< F1,F2>(f1,f2);
}
#else
template< class ... Fs>
struct overload;

template< class F0,class ... Frest>
struct overload< F0,Frest ...> :F0,overload< Frest ...> {
overload(F0 f0,Frest ... rest):F0(f0),overload< Frest ...>(rest ...){}

使用F0 :: operator();
};

模板<>
struct overload<> {
overload(){}
};

template< class ... Fs>
auto make_overload(Fs ... fs){
return overload< Fs ...>(fs ...);
}
#endif

#if 0
#define CAP
#define PRINTY()
#else
#define CAP y
#define PRINTY()cout<< int y ==<< y < endl
#endif

int main(int argc,char * argv []){
int y = 123;

auto f = make_overload(
[CAP](int x){cout<int x ==<< x<< endl; PRINTY ;},
[CAP](char * cp){cout<<char * cp ==<< cp<< endl; PRINTY
f(argc);
f(argv [0]);
}


解决方案

存在于共同的范围。这意味着第二个实现无法找到第二个重载,因为你不会从 overload< Frest ...> 导入函数调用操作符到然而,非捕获的lambda类型定义了一个转换操作符到一个函数指针,该函数指针指向一个函数指针。与lambda的函数调用操作符相同的签名。这个转换操作符可以通过名称查找找到,这是当你删除捕获部分时调用的。



正确的实现,捕获lambdas,并且总是调用 operator()而不是转换运算符,应该如下所示:

  template< class ... Fs> 
struct overload;

template< class F0,class ... Frest>
struct overload< F0,Frest ...> :F0,overload< Frest ...>
{
overload(F0 f0,Frest ... rest):F0(f0),overload< Frest ...>(rest ...){}

使用F0 :: operator();
using overload< Frest ...> :: operator();
};

template< class F0>
struct overload< F0> :F0
{
overload(F0 f0):F0(f0){}

使用F0 :: operator
};

template< class ... Fs>
auto make_overload(Fs ... fs)
{
return overload< Fs ...>(fs ...);
}

DEMO


I'm investigating a C++11 idiom which might be called "overloaded lambda":

Overloading n functions with variadic template seemed very appealing to me but it turned out it didn't work with variable capture: any of [&] [=] [y] [&y] (and [this] etc if in a member function) lead to compilation failure: error: no match for call to '(overload<main(int, char**)::<lambda(int)>, main(int, char**)::<lambda(char*)> >) (char*&)' (with my local GCC 4.9.1 and ideone.com GCC 5.1)

On the other hand, the fixed 2-ary case didn't suffer that problem. (Try changing the first #if 0 to #if 1 on ideone.com)

Any ideas on what's happening here? Is this a compiler bug, or am I deviating from the C++11/14 spec?

http://ideone.com/dnPqBF

#include <iostream>
using namespace std;

#if 0
template <class F1, class F2>
struct overload : F1, F2 {
  overload(F1 f1, F2 f2) : F1(f1), F2(f2) { }

  using F1::operator();
  using F2::operator();
};

template <class F1, class F2>
auto make_overload(F1 f1, F2 f2) {
  return overload<F1, F2>(f1, f2);
}
#else
template <class... Fs>
struct overload;

template <class F0, class... Frest>
struct overload<F0, Frest...> : F0, overload<Frest...> {
  overload(F0 f0, Frest... rest) : F0(f0), overload<Frest...>(rest...) {}

  using F0::operator();
};

template <>
struct overload<> {
  overload() {}
};

template <class... Fs>
auto make_overload(Fs... fs) {
  return overload<Fs...>(fs...);
}
#endif

#if 0
#define CAP
#define PRINTY()
#else
#define CAP y
#define PRINTY() cout << "int y==" << y << endl
#endif

int main(int argc, char *argv[]) {
    int y = 123;

    auto f = make_overload(
        [CAP] (int x) { cout << "int x==" << x << endl; PRINTY(); },
        [CAP] (char *cp) { cout << "char *cp==" << cp << endl; PRINTY(); });
    f(argc);
    f(argv[0]);
}

解决方案

Overload resolution works only for functions that exist in a common scope. This means that the second implementation fails to find the second overload because you don't import function call operators from overload<Frest...> into overload<F0, Frest...>.

However, a non-capturing lambda type defines a conversion operator to a function pointer with the same signature as the lambda's function call operator. This conversion operator can be found by name lookup, and this is what gets invoked when you remove the capturing part.

The correct implementation, that works for both capturing and non-capturing lambdas, and that always calls operator() instead of a conversion operator, should look as follows:

template <class... Fs>
struct overload;

template <class F0, class... Frest>
struct overload<F0, Frest...> : F0, overload<Frest...>
{
    overload(F0 f0, Frest... rest) : F0(f0), overload<Frest...>(rest...) {}

    using F0::operator();
    using overload<Frest...>::operator();
};

template <class F0>
struct overload<F0> : F0
{
    overload(F0 f0) : F0(f0) {}

    using F0::operator();
};

template <class... Fs>
auto make_overload(Fs... fs)
{
    return overload<Fs...>(fs...);
}

DEMO

这篇关于C ++ 11“过载lambda”与可变模板和变量捕获的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

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