Будет ли этот код C ++ всегда работать так, как я ожидаю, или порядок выполнения не гарантирован? - PullRequest
5 голосов
/ 03 апреля 2020

ОК, у меня есть код, который работает, но я не уверен, что он всегда будет работать. Я перемещаю unique_ptr в карту stl, используя один из членов класса в качестве ключа карты, но я не уверен, может ли перемещение сделать недействительным указатель в некоторых ситуациях.

Код эти строки:

struct a
{
    std::string s;
};
std::map<std::string, std::unique_ptr<a>> m;
std::unique_ptr<a> p = std::make_unique<a>();

// some code that does stuff

m[p->s] = std::move(p);

Так что в настоящее время это работает, но мне кажется, что p может стать недействительным до того, как строка будет использована в качестве ключа карты, и это приведет к исключению памяти. Очевидно, я мог бы создать временную строку перед перемещением или назначить ее с помощью итератора, но я бы предпочел этого не делать, если в этом нет необходимости.

Ответы [ 2 ]

13 голосов
/ 03 апреля 2020

Этот код имеет четко определенное поведение.

В C ++ 17, std::move(p) будет оцениваться до m[p->s]. До C ++ 17 std::move(p) можно было оценить до или после m[p->s]. Однако это не имеет значения, потому что std::move(p) не изменяет p. Только присвоение фактически приводит к перемещению p из.

Вызываемый оператор присваивания имеет сигнатуру

unique_ptr& operator=(unique_ptr&& other);

и вызывается так, как если бы он был

m[p->s].operator=(std::move(p));

Это означает, что изменение p гарантированно не произойдет, пока не будет введено тело из operator= (инициализация параметра other является просто привязкой ссылки ). И, конечно, тело operator= не может быть введено до тех пор, пока не будет оценено выражение m[p->s].

Таким образом, ваш код четко определен во всех версиях C ++.

5 голосов
/ 03 апреля 2020

Код в порядке. В C ++ 17, нам дали строгие гарантии на последовательность , что делает этот код на 100% нормальным.

До C ++ 17 стандарт имел

Во всех случаях присвоение упорядочивается после вычисления значения правого и левого операндов и до вычисления значения выражения присваивания.

Но это все еще означает, что код в порядке. Мы не знаем, какой из m[p->s] и std::move(p) произойдет первым, но поскольку move на самом деле ничего не делает с p, p->s будет действительным и будет разрешен до того, как p будет перемещен в m[p->s]

...