我有一个关于lambda的相等比较的问题。 我试过阅读一些参考资料,但我一无所获。
[] (Args ...args) -> ReturnType { ... };
对于这种类型的lambdas,实际上因为它们有空捕获列表而不是闭包,运算符==
和!=
的工作方式与静态函数相同(好吧,似乎编译器会生成它们也是静态函数)。但是对于闭包,任何以相同方式进行比较的尝试都会导致编译错误。
这是一个简单的程序,例如:
#include <typeinfo>
#include <iostream>
struct WrapperBase {
virtual ~WrapperBase() = default;
virtual bool operator==(WrapperBase& v) = 0;
virtual bool operator!=(WrapperBase& v) = 0;
};
template<typename _Tp>
struct Wrapper : WrapperBase {
Wrapper(const _Tp& v) : value(v) { }
bool operator==(WrapperBase& v) override {
try {
Wrapper<_Tp>& vv = dynamic_cast<Wrapper<_Tp>&>(v);
return value == vv.value;
}
catch(std::bad_cast& err) { }
return false;
}
bool operator!=(WrapperBase& v) override {
try {
Wrapper<_Tp>& vv = dynamic_cast<Wrapper<_Tp>&>(v);
return value != vv.value;
}
catch(std::bad_cast& err) { }
return true;
}
//
_Tp value;
};
template<typename _Tp>
WrapperBase* create_wrapper(const _Tp& v) {
return new Wrapper<_Tp>(v);
}
struct Base {
Base(int a, int b) : wrapper(nullptr), a(a), b(b) { }
virtual ~Base() { delete wrapper; }
virtual WrapperBase* create_wrapper() = 0;
WrapperBase* wrapper;
int a;
int b;
};
struct ClassA : Base {
ClassA(int a, int b) : Base(a, b) {
wrapper = create_wrapper();
}
WrapperBase* create_wrapper() override {
auto lambda = [] (int v1, int v2) { return v1 + v2; };
return ::create_wrapper(lambda);
}
};
struct ClassB : Base {
ClassB(int a, int b) : Base(a, b) {
wrapper = create_wrapper();
}
WrapperBase* create_wrapper() override {
auto lambda = [=] (int v1, int v2) { return a + b + v1 + v2; };
return ::create_wrapper(lambda);
}
};
int main(int argc, char** argv) {
std::cout << std::boolalpha;
// all works fine:
ClassA a1(1, 2);
ClassA a2(3, 4);
std::cout << (*a1.wrapper == *a1.wrapper) << std::endl; // true
std::cout << (*a2.wrapper == *a2.wrapper) << std::endl; // true
std::cout << (*a1.wrapper == *a2.wrapper) << std::endl; // true
// cause compilation error:
ClassB b1(1, 2);
ClassB b2(3, 4);
std::cout << (*b1.wrapper == *b1.wrapper) << std::endl;
std::cout << (*b2.wrapper == *b2.wrapper) << std::endl;
std::cout << (*b1.wrapper == *b2.wrapper) << std::endl;
return 0;
}
比较在ClassA实例中创建的lambda总是返回true
,即使它们是在不同的上下文中创建的(就像我说的那样)。另一方面,ClassB甚至不编译,因为找不到运算符==
和!=
的lambda。
看起来,这个程序的格式不正确,并且以我尝试的方式比较lambda会导致程序的未定义行为。但如果它确实是未定义的行为,它们如何进行比较呢? (我猜,不管怎样)
答案 0 :(得分:4)
对于这种类型的lambda,实际上因为它们不是闭包 有空捕获列表,运算符==和!=的工作方式与 对于静态函数(好吧,似乎编译器将它们生成为 静态函数)。
它的工作原理是因为没有捕获的lambda的闭包类型提供了一个返回函数指针的转换操作符。那些是可比的。 [expr.prim.lambda] / 6(强调我的):
lambda-expression 的闭包类型,没有 lambda-capture 具有公共非虚拟非显式 const转换函数 指向具有相同参数和返回类型的函数的指针 闭包类型的函数调用操作符。这个值返回的值 转换函数应该是函数的地址,当时 invoked,与调用闭包类型的函数具有相同的效果 呼叫运营商。
(如果转换运算符是明确的,则比较不起作用)
粗略地说,[] {}
形式的lambda转换为
struct closure_type
{
private:
static void call() {}
public:
// closure_type() = delete; // Commented for the sake of the demo
closure_type& operator=(closure_type const&) = delete;
void operator()() const { /*return call();*/ }
operator decltype(&call)() const
{
return &call;
}
};
您可能已经注意到,转换运算符每次都返回相同的函数指针。虽然如果发生类似的事情会发生异常令人惊讶,但标准确实允许返回不同的函数指针,以便为同一个闭包对象调用转换运算符。因此,两个闭包对象的比较具有实现定义的值。 (但是,对于两个相同类型的闭包对象,它应该在所有实现上都是true
。)