当我运行此 C++ 代码时:
#include <iostream>
#include <vector>
using namespace std;
int main()
{
vector<int> vec = [&] ()
{
cout<<"pre vec.size() = "<< vec.size() <<endl;
vector<int> retval = {};
for(int i = 0; i < 10; ++i)
vec.push_back(i); // I typed "vec", not "retval"
cout<<"vec.size() = "<< vec.size() <<endl;
cout<<"retval.size() = "<< retval.size() <<endl;
return retval;
}();
cout<< vec.size() <<endl;
}
我得到输出:pre vec.size() = 34354494244
vec.size() = 10
retval.size() = 10
10
在 lambda 内部, vec 似乎首先未初始化(请参阅大小)。为什么 push_backing 不会导致(valgrind)错误?循环后,vec 的大小为 10,可以。但是为什么 retval 的大小也已经是 10(而不是 0)了?
由于我在 lambda 中填充了 vec,但返回了空的 retval,为什么 vec 在评估后的大小为 10(而不是 0)?
我尝试了 g++ 9.3.0 和 clang++ 10.0.0 并获得了行为。
最佳答案
尝试对您的程序进行小幅调整。添加打印两个 vector 地址的语句:
#include <iostream>
#include <vector>
using namespace std;
int main()
{
vector<int> vec = [&] ()
{
cout<<"pre vec.size() = "<< vec.size() <<endl;
vector<int> retval = {};
std::cout << &retval << " = " << &vec << std::endl;
for(int i = 0; i < 10; ++i)
vec.push_back(i); // I typed "vec", not "retval"
cout<<"vec.size() = "<< vec.size() <<endl;
cout<<"retval.size() = "<< retval.size() <<endl;
return retval;
}();
cout<< vec.size() <<endl;
}
gcc 显示 同 两者的地址 retval
和 vec
!这是named return value optimization的结果,这是允许的。这就是 valgrind 无法检测到这种未定义行为的原因。未定义的行为意味着“任何事情都可能发生”,包括程序按预期工作。这就是这里发生的事情:vec
的实际构造发生在 retval
被构造,并且从 lambda 返回的结果没有发生复制。载体已经构建好了。但是请注意,命名返回值优化是可选的,不需要实现。您不能依赖每个编译器都以这种方式工作,而将未定义行为的原始问题放在一边。
关于立即计算的 lambda 的 C++ 范围规则,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/64171181/