c ++ lambda在错误的地址/错误的上下文中执行
c++ lambda executed at wrong address / in wrong context
以下代码也可在 godbolt.org/z/SjkAWZ 获得。
我有一个带有 std::function 作为成员变量的结构,它由构造函数中的 lambda 表达式分配。
可以从构造函数成功调用 lambda,但不能从成员函数调用。
#include <vector>
#include <functional>
struct S
{
S()
{
std::cout << "construct: " << reinterpret_cast<long>(this) << " " << std::flush;
std::cout << m_ << '\n';
lambda_ = [&]() {
std::cout << "lambda: " << reinterpret_cast<long>(this) << " " << std::flush;
std::cout << m_ << '\n';
};
lambda_();
}
void foo()
{
std::cout << "foo: " << reinterpret_cast<long>(this) << " " << std::flush;
std::cout << m_ << '\n';
lambda_();
}
private:
int m_ = 3; // some member variable
std::function<void(void)> lambda_;
};
int main()
{
std::vector<S> v;
v.emplace_back();
v.emplace_back();
std::cout << "\n--- loop ---\n";
for(auto& b : v)
{ b.foo(); }
}
可能的程序输出:
construct: 94159000759920 3
lambda: 94159000759920 3
construct: 94159000761048 3
lambda: 94159000761048 3
--- loop ---
foo: 94159000761008 3
lambda: 94159000759920 0 // <-- bad *this* address, bad value for m_
foo: 94159000761048 3
lambda: 94159000761048 3 // <-- bad *this* address, correct value for m_ (coincidence?)
如您所见,如果从成员函数 foo 调用 lambda 函数的上下文 this
是错误的。
有趣的是,如果我只在向量中放置一个元素,情况就不是这样了:
construct: 13753968 3
lambda: 13753968 3
--- loop ---
foo: 13753968 3
lambda: 13753968 3 // <-- correct *this* address and correct value for m_
我在 Debian 上使用 g++ 8.3.0 版。
我是否通过在适当的位置构造向量中的元素而导致未定义的行为?
您错过了结构的复制/移动构造函数!
如果您将一些变量分配给 std::vector
,它有时会保留新内存并将所有内容复制到新内存。在您的情况下,lambda 不知道更改并保留对 S
的旧实例的引用。所以你的代码完全被破坏了。
您可以通过在分配内容之前预留足够的 space 来解决此问题:
int main()
{
std::vector<S> v;
v.reserve(5);
...
}
但这只是解决方法!您应该准备一个 move/copy 构造函数,以便能够获得实例的正确副本。
以下代码也可在 godbolt.org/z/SjkAWZ 获得。
我有一个带有 std::function 作为成员变量的结构,它由构造函数中的 lambda 表达式分配。
可以从构造函数成功调用 lambda,但不能从成员函数调用。
#include <vector>
#include <functional>
struct S
{
S()
{
std::cout << "construct: " << reinterpret_cast<long>(this) << " " << std::flush;
std::cout << m_ << '\n';
lambda_ = [&]() {
std::cout << "lambda: " << reinterpret_cast<long>(this) << " " << std::flush;
std::cout << m_ << '\n';
};
lambda_();
}
void foo()
{
std::cout << "foo: " << reinterpret_cast<long>(this) << " " << std::flush;
std::cout << m_ << '\n';
lambda_();
}
private:
int m_ = 3; // some member variable
std::function<void(void)> lambda_;
};
int main()
{
std::vector<S> v;
v.emplace_back();
v.emplace_back();
std::cout << "\n--- loop ---\n";
for(auto& b : v)
{ b.foo(); }
}
可能的程序输出:
construct: 94159000759920 3
lambda: 94159000759920 3
construct: 94159000761048 3
lambda: 94159000761048 3
--- loop ---
foo: 94159000761008 3
lambda: 94159000759920 0 // <-- bad *this* address, bad value for m_
foo: 94159000761048 3
lambda: 94159000761048 3 // <-- bad *this* address, correct value for m_ (coincidence?)
如您所见,如果从成员函数 foo 调用 lambda 函数的上下文 this
是错误的。
有趣的是,如果我只在向量中放置一个元素,情况就不是这样了:
construct: 13753968 3
lambda: 13753968 3
--- loop ---
foo: 13753968 3
lambda: 13753968 3 // <-- correct *this* address and correct value for m_
我在 Debian 上使用 g++ 8.3.0 版。
我是否通过在适当的位置构造向量中的元素而导致未定义的行为?
您错过了结构的复制/移动构造函数!
如果您将一些变量分配给 std::vector
,它有时会保留新内存并将所有内容复制到新内存。在您的情况下,lambda 不知道更改并保留对 S
的旧实例的引用。所以你的代码完全被破坏了。
您可以通过在分配内容之前预留足够的 space 来解决此问题:
int main()
{
std::vector<S> v;
v.reserve(5);
...
}
但这只是解决方法!您应该准备一个 move/copy 构造函数,以便能够获得实例的正确副本。