Почему использование временного объекта в базе данных для инициализации приводит к сбою? - программирование
Подтвердить что ты не робот

Почему использование временного объекта в базе данных для инициализации приводит к сбою?

Почему следующий код разбивается как на Visual Studio, так и на GCC?

Для его аварийного восстановления требуется диапазон для цикла, std:: map, std::string и ссылки на строку. Если я удалю кого-нибудь из них, он будет работать.

#include <iostream>
#include <string>
#include <map>
using namespace std;

struct S
{
    map<string, string> m;

    S()
    {
        m["key"] = "b";
    }

    const string &func() const
    {
        return m.find("key")->second;
    }
};

int main()
{
    for (char c : S().func())
        cout << c;

    return 0;
}

Идеальная ссылка: http://ideone.com/IBmhDH

4b9b3361

Ответ 1

Линия инициализации диапазона цикла for(:) не продлевает срок службы ничего, кроме последнего временного (если есть). Любые другие временные файлы отбрасываются до выполнения цикла for(:).

Теперь, не отчаивайтесь; есть легкое решение этой проблемы. Но сначала пройдите через то, что идет не так.

Код for(auto x:exp){ /* code */ } расширяется, в основном:

{
  auto&& __range=exp;
  auto __it=std::begin(__range);
  auto __end=std::end(__range);
  for(; __it!=__end;++__it){
    auto x=*__it;
    /* code */
  }
}

(С скромной ложью на строках __it и __end, а все переменные, начиная с __, не имеют видимого имени. Также я показываю версию на С++ 17, потому что я верю в лучший мир, и различия здесь не имеют значения.)

Ваш exp создает временный объект, а затем возвращает ссылку внутри него. Временное умирает после этой строки, поэтому в остальной части кода у вас есть оборванная ссылка.

Фиксация относительно проста. Чтобы исправить это:

std::string const& func() const& // notice &
{
    return m.find("key")->second;
}
std::string func() && // notice &&
{
    return std::move(m.find("key")->second);
}

делать rvalue перегрузки и возвращать перемещенные значения по значению при использовании временных времен, вместо того, чтобы возвращать ссылки в них.

Тогда

auto&& __range=exp;
Строка

ссылается на расширение жизненного цикла на возвращаемое значение string и больше не обматывает ссылки.

Как правило, никогда не возвращайте диапазон по ссылке на параметр, который может быть значением r.


Приложение: Подождите, && и const& после методов? rvalue ссылки на *this?

С++ 11 добавлены ссылки rvalue. Но функция this или self для функций является специальной. Чтобы выбрать, какая перегрузка метода основана на rvalue/lvalue-ness вызываемого объекта, вы можете использовать & или && после окончания метода.

Это работает так же, как тип параметра для функции. && после того, как метод заявляет, что метод следует вызывать только на неконстантных значениях; const& означает, что он должен вызываться для постоянных lvalues. Вещи, которые точно не совпадают, соответствуют обычным правилам соблюдения правил.

Когда у вас есть метод, который возвращает ссылку в объект, убедитесь, что вы улавливаете временные файлы с перегрузкой && и либо не возвращаете ссылку в этих случаях (возвращаете значение), либо =delete метод.

Ответ 2

S().func()

Это создает временный объект и вызывает метод, возвращающий ссылку на std::string, принадлежащую (косвенно) временным объектом (std::string находится в контейнере, являющемся частью временного объекта).

После получения ссылки временный объект будет уничтожен. Это также разрушает std::string, который принадлежал (косвенно) временным объектом.

После этой точки дальнейшее использование ссылочного объекта становится undefined. Такие, как итерация по содержанию.

Это очень распространенная ошибка, когда дело доходит до использования итерации диапазона. С уважением, вы также виноваты в том, что с этим справились.