Итерация по контейнеру rvalue

Вызывает ли следующий код неопределенное поведение?

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

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

Если не определено, каков хороший способ это исправить? Что делать, если я использую цикл ++ для диапазона C ++ 11 вместо BOOST_FOREACH?

3

Решение

К сожалению, это, скорее всего, неопределенное поведение.

Проблема в том, что у вас есть два уровня здесь:

  1. std::map<...> является r-значением, его время жизни будет увеличено до конца полного выражения
  2. 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 заканчивает тем, что указывает в пустоту.

3

Другие решения

Возвращаемое значение существует до конца полного выражения
который создает это. Так что все зависит от того, как BOOST_FOREACH
расширяется; если он создает область вне цикла for, и
копирует возвращаемое значение в переменную в нем (или использует его для
инициализировать ссылку), тогда вы в безопасности. Если это не так,
вы не.

Цикл диапазона C ++ 11 в основном имеет семантику связывания
к ссылке в области видимости за пределами классического цикла for, так что это
должен быть в безопасности.

РЕДАКТИРОВАТЬ:

Это будет применяться, если вы захватываете возвращаемое значение
foo, Как указывает Бенджамин Линдли, это не так. Вы
захват возвращаемого значения operator[] на карте. И это
является не временный; это ссылка. Так что без расширения
время жизни, ни в BOOST_FOREACH ни в пределах досягаемости.
Это означает, что сама карта будет уничтожена в конце
полное выражение, которое содержит вызов функции, и что
происходит неопределенное поведение. (Буст мог, я полагаю, сделать глубокий
копия карты, чтобы вы были в безопасности. Но почему-то я сомневаюсь, что это
делает.)

Конец редактирования:

Тем не менее, я бы поставил под сомнение мудрость возвращения
std::map когда все, что вы хотите, это одна запись в нем. Если
карта на самом деле существует вне функции (не в куче),
тогда я бы вернул ссылку на это. В противном случае, я бы нашел некоторые
что это сделал.

2

От: 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;
}
0
По вопросам рекламы [email protected]