Вызывает ли следующий код неопределенное поведение?
std::map<int, vector<int>> foo()
{
return ...
}
BOOST_FOREACH(const int& i, foo()[42])
{
std::cout << i << std::endl;
}
Если не определено, каков хороший способ это исправить? Что делать, если я использую цикл ++ для диапазона C ++ 11 вместо BOOST_FOREACH?
К сожалению, это, скорее всего, неопределенное поведение.
Проблема в том, что у вас есть два уровня здесь:
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
заканчивает тем, что указывает в пустоту.
Возвращаемое значение существует до конца полного выражения
который создает это. Так что все зависит от того, как BOOST_FOREACH
расширяется; если он создает область вне цикла for, и
копирует возвращаемое значение в переменную в нем (или использует его для
инициализировать ссылку), тогда вы в безопасности. Если это не так,
вы не.
Цикл диапазона C ++ 11 в основном имеет семантику связывания
к ссылке в области видимости за пределами классического цикла for, так что это
должен быть в безопасности.
РЕДАКТИРОВАТЬ:
Это будет применяться, если вы захватываете возвращаемое значение
foo
, Как указывает Бенджамин Линдли, это не так. Вы
захват возвращаемого значения operator[]
на карте. И это
является не временный; это ссылка. Так что без расширения
время жизни, ни в BOOST_FOREACH
ни в пределах досягаемости.
Это означает, что сама карта будет уничтожена в конце
полное выражение, которое содержит вызов функции, и что
происходит неопределенное поведение. (Буст мог, я полагаю, сделать глубокий
копия карты, чтобы вы были в безопасности. Но почему-то я сомневаюсь, что это
делает.)
Конец редактирования:
Тем не менее, я бы поставил под сомнение мудрость возвращения
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;
}