C++11“重载的lambda"带有可变参数模板和变量捕获
我正在研究一个 C++11 习语,它可能被称为重载 lambda":
I'm investigating a C++11 idiom which might be called "overloaded lambda":
- http://cpptruths.blogspot.com/2014/05/fun-with-lambdas-c14-style-part-2.html
- http://martinecker.com/martincodes/lambda-expression-overloading/
使用可变参数模板重载 n 函数对我来说似乎很有吸引力,但事实证明它不适用于变量捕获:any of [&]
[=]
[y]
[&y]
(和 [this]
等,如果在成员函数中)导致编译失败:<代码>错误:没有匹配的调用 '(overload<main(int, char**)::<lambda(int)>, main(int, char**)::<lambda(char*)>>) (char*&)'(使用我本地的 GCC 4.9.1 和 ideone.com GCC 5.1)
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)
另一方面,固定的二元情况没有遇到这个问题.(尝试将 ideone.com 上的第一个 #if 0
更改为 #if 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)
对这里发生的事情有什么想法吗?这是编译器错误,还是我偏离了 C++11/14 规范?
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
导入 overload
.
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...>
.
但是,非捕获 lambda 类型将转换运算符定义为与 lambda 的函数调用运算符具有相同签名的函数指针.这个转换操作符可以通过名称查找找到,这就是当你删除捕获部分时调用的内容.
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.
适用于捕获和非捕获 lambda 并且始终调用 operator()
而不是转换运算符的正确实现应如下所示:
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...);
}
演示
在c++17,有了类模板参数推导和using
声明的包扩展,上面的实现可以简化为:
In c++17, with class template argument deduction and pack expansion of using
declarations in place, the above implementation can be simplified to:
template <typename... Ts>
struct overload : Ts... { using Ts::operator()...; };
template <typename... Ts>
overload(Ts...) -> overload<Ts...>;
演示 2
相关文章