以下代码是否导致未定义的行为?
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怎么办?
答案 0 :(得分:3)
不幸的是,这很可能是未定义的行为。
问题在于你有两个级别:
std::map<...>
是一个r值,它的生命周期将扩展到完整表达式结束std::vector<int>&
是l值引用(进入对象),其生命周期是对象的生命周期。问题出现是因为代码(大致)扩展为类似:
// 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
最终指向幽冥。
答案 1 :(得分:2)
返回值一直存在,直到完整表达式结束
创造它。所以这一切都取决于BOOST_FOREACH
的方式
扩展;如果它在for循环之外创建一个范围,并且
将返回值复制到其中的变量(或使用它
初始化参考),那么你是安全的。如果没有,
你不是。
C ++ 11 range-for循环基本上具有绑定的语义 在经典for循环之外的范围内的引用,所以它 应该是安全的。
编辑:
如果您正在捕获的返回值,这将适用
foo
。正如本杰明林德利指出的那样,你不是。你是
在地图上捕获operator[]
的返回值。还有这个
是不是临时的;这是一个参考。所以没有延伸
生命周期发生,既不在BOOST_FOREACH
也不在范围内。
这意味着地图本身将在最后被破坏
包含函数调用的完整表达式,以及
发生未定义的行为。 (我想,Boost可以做得很深
地图的副本,所以你是安全的。但不知何故,我怀疑它
一样。)
编辑结束:
从来没有,我会怀疑回归的智慧
std::map
当你想要的只是一个条目时。如果
map实际上存在于函数外部(不在堆上),
然后我会返回它的引用。否则,我会找到一些
它做了什么。
答案 2 :(得分:0)
来自: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;
}