以下代码是否导致未定义的行为?
std::map<int, vector<int>> foo()
{
return ...
}
BOOST_FOREACH(const int& i, foo()[42])
{
std::cout << i << std::endl;
}
如果未定义,修复它的好方法是什么?如果我使用 c++11 range-for 循环而不是 BOOST_FOREACH 会怎样?
以下代码是否导致未定义的行为?
std::map<int, vector<int>> foo()
{
return ...
}
BOOST_FOREACH(const int& i, foo()[42])
{
std::cout << i << std::endl;
}
如果未定义,修复它的好方法是什么?如果我使用 c++11 range-for 循环而不是 BOOST_FOREACH 会怎样?
不幸的是,这很可能是未定义的行为。
问题是您在这里有两个级别:
std::map<...>
是一个 r 值,它的生命周期将被扩展直到完整表达式结束std::vector<int>&
是一个左值引用(到一个对象),它的生命周期是对象的生命周期。出现问题是因为代码(大致)扩展为:
// from
for (<init>: <expr>) {
<body>
}
// to
auto&& __container = <expr>;
for (auto __it = begin(container), __e = end(container); __it != __e; ++__it)
{
<init> = *__it;
<body>
}
这里的问题在于初始化__container
:
auto&& __container = foo()[42];
如果它只是foo()
,这将起作用,因为 的生命周期std::map<...>
将延长以匹配的生命周期__container
,但是在这种情况下,我们得到:
// non-standard gcc extension, very handy to model temporaries:
std::vector<int>& __container = { std::map<...> m = foo(); m[42] };
因此__container
最终指向下界。
返回值一直存在,直到创建它的完整表达式结束。所以这一切都取决于如何BOOST_FOREACH
扩展;如果它在 for 循环之外创建了一个范围,并将返回值复制到其中的一个变量(或使用它来初始化一个引用),那么你是安全的。如果没有,你就不是。
C++11 range-for 循环基本上具有绑定到经典 for 循环范围之外的引用的语义,因此它应该是安全的。
编辑:
如果您要捕获 的返回值,这将适用
foo
。正如本杰明林德利指出的那样,你不是。您正在获取地图上的返回值operator[]
。这不是暂时的;这是一个参考。因此,无论在范围内还是范围内,都不会延长寿命BOOST_FOREACH
。这意味着映射本身将在包含函数调用的完整表达式的末尾被破坏,并且会发生未定义的行为。(我想,Boost 可以制作地图的深层副本,这样你就安全了。但不知何故,我怀疑它确实如此。)
编辑结束:
尽管如此,
std::map
当你想要的只是一个条目时,我会质疑返回一个是否明智。如果映射实际上存在于函数之外(不在堆上),那么我会返回对它的引用。否则,我会找到一些它的作用。
来自: http: //www.boost.org/doc/libs/1_55_0/doc/html/foreach.html
遍历按值返回序列的表达式(即右值):
extern std::vector<float> get_vector_float();
BOOST_FOREACH( float f, get_vector_float() )
{
// Note: get_vector_float() will be called exactly once
}
所以它定义明确并且有效。
同样,它在 C++11 中得到了很好的定义(并且有效):
for (const int& i : get_vector()) // get_vector() computed only once
{
std::cout << i << std::endl;
}
这里的问题是从临时(通过方法)foo()[42]
返回引用。
auto& v = foo()[42];
foo()
暂时的寿命不会延长...
您可以通过延长foo
临时寿命来解决这个问题
auto&& m = foo();
for (const int& i : m[42]) {
std::cout << i << std::endl;
}