您的代码的某些部分似乎毫无意义。我将通过它们并将它们一一剥离。
A operator+(const A& a1) {
return (+[](const A& a1, const A& a2) -> A {
return A(a1.val + a2.val);
})(*this, a1);
};
首先,+[] 只是将它不必要地衰减为函数指针。除了可能混淆优化器之外,它在这里没有任何用处。我们也可以去掉()s:
A operator+(const A& a1) {
return [](const A& a1, const A& a2) -> A {
return A(a1.val + a2.val);
}(*this, a1);
};
现在,->A 部分是噪音,因为它可以推断返回类型:
A operator+(const A& a1) {
return [](const A& a1, const A& a2) {
return A(a1.val + a2.val);
}(*this, a1);
};
接下来,为什么要使用会员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);
};
这消除了您引入的编号混乱(其中一个范围内的 a1 在另一个范围内是 a2,并且引入了一个名为 a1 的新变量,它引用了 *this)。
最后,此时 lambda 什么都不做:
friend A operator+(const A& a1, const A& a2) {
return A(a1.val + a2.val);
};
删除它会使代码更清晰。
现在谈谈你的问题。
在您完成这些简化步骤时,编译器更有可能优化 lambda。我怀疑你做的最糟糕的事情是一元 +,它将 lambda 转换为函数指针:我知道 gcc 擅长内联函数指针,最后我检查了 MSVC 不擅长它。然而,每个编译器都擅长内联对无状态 lambda 的调用。
在调用点,被调用的方法对于编译器来说是众所周知的类型系统,所以不必分析函数指针的提供。数据被复制到参数然后使用,通常在一个小函数中。这很容易内联。
即使您有适度的捕获要求,只要您不键入擦除或复制 lambda,您就会有一堆本地引用或本地变量的副本,它们在 lambda 的主体中使用。易于内联。
现在,我的答案第一部分中的每个简化步骤都使代码更简单,直到最后,当 lambda 消失并且唯一要“优化”的是一个简单的 RVO(您必须使用编译器标志来抑制它)以防止它发生),也就是在 return 语句中省略了临时的 A。
如果您的A 具有来自.val+.val 的结果的隐式构造函数,那么您甚至不需要在return 语句中使用A。
没有一种简单的方法可以用 lambdas 来编写运算符。您需要将所说的 lambda 存储在某个地方,然后将 operator+(或其他任何东西)粘贴到它上面,这两个步骤将需要更多的代码,而不仅仅是将主体直接注入 operator+。
我可以做一些花哨的 decltype 和宏废话,让您将全局 lambda 绑定到某个类的给定 operator+ 的主体(使用 CRTP 和特征类),但称其为“更简单”不仅仅是有点牵强。
它会让你做类似的事情:
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 );
或类似的,甚至期望 << 被内联。
再一次,考虑到这个“更简单”有点牵强。