Iterate through rvalue container

Is the following code causing undefined behavior?

std::map<int, vector<int>> foo()
{
return ...
}

BOOST_FOREACH(const int& i, foo()[42])
{
std::cout << i << std::endl;
}

If undefined, what is a good way to fix it? What if I use a C ++ 11 range-for loop instead of BOOST_FOREACH?

+3
source share
3 answers

This, unfortunately, is most likely undefined behavior.

The problem is that you have two levels:

  • std::map<...> is an r-value, its lifetime will be extended to the end of the full expression
  • std::vector<int>& is a reference to an l-value (to an object), its lifetime is an object.

The problem arises because the code (approximately) expands to the following value:

// from
for (<init>: <expr>) {
    <body>
}

// to
auto&& __container = <expr>;
for (auto __it = begin(container), __e = end(container); __it != __e; ++__it)
{
    <init> = *__it;
    <body>
}

The problem here is in the initialization __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 , .

+3

. , BOOST_FOREACH ; for ( ), . , .

++ 11 , .

EDIT:

, foo. , . operator[] . ; . BOOST_FOREACH, . , , , undefined. (, Boost , . - , .)

:

, std::map, , , . ( ), . .

+2

: http://www.boost.org/doc/libs/1_55_0/doc/html/foreach.html

, (.. r):

extern std::vector<float> get_vector_float();
BOOST_FOREACH( float f, get_vector_float() )
{
    // Note: get_vector_float() will be called exactly once
}

So, it is well defined and works.

Furthermore, it is well defined in C ++ 11 (and works):

for (const int& i : get_vector()) // get_vector() computed only once
{
    std::cout << i << std::endl;
}

The problem is that it foo()[42]returns a link from a temporary (using the method).

auto& v = foo()[42];

service life is foo()not temporarily extended ...

You can solve this by extending your footime span.

auto&& m = foo();

for (const int& i : m[42]) {
    std::cout << i << std::endl;
}
0
source

All Articles