C++11 "overloaded lambda",带可变参数模板和变量捕获
C++11 "overloaded lambda" with variadic template and variable capture
我正在研究c++ 11的一个习惯用法,可以称为"重载lambda":
- http://cpptruths.blogspot.com/2014/05/fun -与-λ- c14 -风格的一部分- 2. - html
- http://martinecker.com/martincodes/lambda-expression-overloading/
用可变模板重载n函数对我来说似乎很有吸引力,但事实证明它不适用于变量捕获:任何[&]
[=]
[y]
[&y]
(和[this]
等如果在成员函数中)导致编译失败:error: no match for call to '(overload<main(int, char**)::<lambda(int)>, main(int, char**)::<lambda(char*)> >) (char*&)'
(与我的本地GCC 4.9.1和ideone.com GCC 5.1)
另一方面,固定的2元情况没有这个问题。(尝试在ideone.com上更改第一个#if 0
为#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 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<Frest...>
导入函数调用操作符到overload<F0, Frest...>
。
然而,非捕获lambda类型定义了一个函数指针的转换操作符,其签名与lambda的函数调用操作符相同。该转换操作符可以通过名称查找找到,这是在删除捕获部分时调用的操作符。
正确的实现,适用于捕获和非捕获lambda,并且总是调用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...) {}
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...);
}
在c++17中,有了类模板参数推导和using
声明的包扩展,上述实现可以简化为:
template <typename... Ts>
struct overload : Ts... { using Ts::operator()...; };
template <typename... Ts>
overload(Ts...) -> overload<Ts...>;
演示2
c++ 11中扁平版本的重载
回复对已接受答案的评论,这里有一个完全不使用递归模板的版本。这允许尽可能多的重载,只要你需要,只调用一个侧模板。
namespace details {
template<class F>
struct ext_fncall : private F {
ext_fncall(F v) :
F(v) {}
using F::operator();
};
}
template<class... Fs>
struct overload : public details::ext_fncall<Fs>... {
overload(Fs... vs) :
details::ext_fncall<Fs>(vs)... {}
};
template<class... Fs>
overload<Fs...> make_overload(Fs... vs) {
return overload<Fs...> {vs...};
}
<标题>侧模板ext_fncall<class F>
派生自一个给定的函子,并且只公开它的operator()
,它模仿了给定的c++ 11版本。
实际的overload<class... Fs>
派生自ext_fncall<Fs>...
,这意味着它只从它派生的类中公开operator()
(由于ext_fncall<F>
,其他成员不能被访问)。
- 如何将C++闭包与变量参数同时重用——类似于JavaScript
- 扩展可变参数模板中的变量名称
- 在类构造函数中定义结构变量的参数
- 带有整数的变量参数列表
- 转发变量参数列表以模拟 std::thread
- 如何在不传递命令行参数的情况下在 c++ 中设置环境变量
- 存储稍后要转发的变量参数
- C++具有模板成员变量的类. 和参数内存输出
- 我应该如何在没有变量的情况下将相同的参数传递给 CMAKE 中的多个目标?
- 为变量模板的每个参数调用模板函数
- 具有推导参数的模板函数指针数组变量
- 函数参数变量总是需要 & 或 * 运算符吗?
- 为什么 beginthreadex 线程参数变量在父线程中没有更新
- 在 Metal 着色器代码中,如何定义函数的 in/out 参数变量?
- 如何在函数中使用非参数变量?
- 即将将引用作为函数中的参数传递以更改参数变量的值
- 基于不断变化的参数/变量的XML get值
- cocos2d-x v3 CallFunc作为参数/变量如何
- 在编译时使用c++元编程中的运行时参数(变量)
- 我可以返回一个非参数变量吗?