syn*_*gma 0 c++ lambda operator-overloading c++11
考虑这个例子:
struct A {
int val;
A(int val) : val(val){};
A operator+(const A& a1) {
return (+[](const A& a1, const A& a2) -> A {
return A(a1.val + a2.val);
})(*this, a1);
};
};
int main(int argc, char* argv[]) {
A a(14);
A b(12);
auto c = a + b;
return 0;
}
Run Code Online (Sandbox Code Playgroud)
我有两个问题:
operator+
或者是否会有任何开销?代码的某些部分似乎毫无意义.我会仔细检查它们并将它们逐个剥离.
A operator+(const A& a1) {
return (+[](const A& a1, const A& a2) -> A {
return A(a1.val + a2.val);
})(*this, a1);
};
Run Code Online (Sandbox Code Playgroud)
首先,+[]
简单地将它不必要地衰减到函数指针.除了可能混淆优化器之外,这里没什么用处.我们也可以摆脱()
s:
A operator+(const A& a1) {
return [](const A& a1, const A& a2) -> A {
return A(a1.val + a2.val);
}(*this, a1);
};
Run Code Online (Sandbox Code Playgroud)
现在,该->A
部分是噪音,因为它可以推断出返回类型:
A operator+(const A& a1) {
return [](const A& a1, const A& a2) {
return A(a1.val + a2.val);
}(*this, a1);
};
Run Code Online (Sandbox Code Playgroud)
接下来,为什么使用会员operator+
? +
是对称的,通过使用Koenig风格的运算符,这变得更加明显:
friend A operator+(const A& a1, const A& a2) {
return [](const A& a1, const A& a2) {
return A(a1.val + a2.val);
}(a1, a2);
};
Run Code Online (Sandbox Code Playgroud)
它摆脱了你引入的编号混淆(a1
在一个范围内a2
的另一个范围内,并且a1
引入了一个引用的新变量*this
).
最后,lambda在这一点上什么都不做:
friend A operator+(const A& a1, const A& a2) {
return A(a1.val + a2.val);
};
Run Code Online (Sandbox Code Playgroud)
并删除它导致更清晰的代码.
现在关于你的问题.
在您完成这些简化步骤时,编译器更有可能优化lambda.我怀疑你做的最糟糕的事情是一元+
,它将lambda转换为一个函数指针:我知道gcc擅长内联函数指针,最后我检查了MSVC是不好的.但是,每个编译器都擅长内联对无状态lambda的调用.
在调用时,被调用的方法对于编译器而言是类型系统所熟知的,因此不必对函数指针的上帝进行分析.数据被复制到随后使用的参数中,通常在一个小函数中.这很容易内联.
即使您有适度的捕获要求,只要您不键入擦除或复制lambda,就会有一堆本地引用或lambda主体中使用的局部变量的副本.易于内联.
现在,我的答案的第一部分中的每个简化步骤使代码更简单,直到最后,当lambda消失并且"优化"的唯一事情是一个简单的RVO(你必须用编译器标志来抑制它以防止它发生的事情),也就是A
在return
声明中忽略了临时性.
如果你A
的结果中有一个隐式构造函数.val+.val
,你甚至不需要A
在return
语句中.
没有一种简单的方法可以用lambdas来编写运算符.你需要在某个地方存储所述lambda,然后将它operator+
(或其他)粘合到它上面,这两个步骤将需要更多的代码,而不仅仅是直接将体注入operator+
.
我可以做一些花哨的decltype和宏废话,让你将一个全局lambda绑定到operator+
某个类的给定体(使用CRTP和traits类),但调用"更简单"不仅仅是一个延伸.
它可以让你做一些事情:
const auto print_via_to_string = [](std::ostream& os, auto&& val){
using std::to_string;
os << to_string(decltype(val)(val));
};
struct Foo { /* ... */ };
BIND_OPERATOR( <<, std::ostream&, Foo const&, print_via_to_string );
Run Code Online (Sandbox Code Playgroud)
或者某些,甚至期望<<
被内联.
再次,考虑到这个"更简单"是一个延伸.