Переместить поведение конструктора
Недавно я наткнулся на какое-то странное поведение (странное с моей точки зрения) в конструкторе ходов. Результат отличается при компиляции с GCC и Visual Studio. Я хотел бы услышать объяснение этого поведения, не думаю, что это ошибка, но, вероятно, зависит от компилятора.
Рассмотрим следующий код:
#include <iostream>
#include <unordered_map>
struct Test
{
std::unordered_map<int, int> v;
std::unordered_map<int, int>::iterator vend;
Test(std::unordered_map<int, int>::iterator &it)
: vend { v.end() }
{
it = this->vend;
};
Test() = delete;
Test(Test const &) = delete;
Test(Test &&) = default; // <- line in question
};
int main()
{
std::unordered_map<int, int>::iterator it;
std::unordered_map<int, Test> m;
m.emplace(0, Test{ it });
std::cout << std::boolalpha << (m.at(0).v.end() == it) << "\n";
return 0;
}
Поэтому я сохраняю итератор до конца карты в элементе карты после создания элемента. Я также беру ссылку на это, чтобы сравнить позже. Из std:: unordered_map:: emplace:
Вставляет новый элемент в контейнер, созданный на месте с заданными аргументами, если в контейнере нет элемента с ключом.
Тщательное использование emplace позволяет создавать новый элемент, избегая ненужных операций копирования или перемещения.
Используя конструктор перемещения по умолчанию, итератор, сохраненный в элементе карты, и моя ссылка одинаковы:
Test(Test &&) = default;
Результаты true
в GCC и true
в ВС Теперь, если я изменю перемещение конструктора на:
Test(Test &&) {}
GCC все еще возвращается true
но VS возвращается false
На всякий случай пробовал с с ++17, те же результаты. Так может кто-нибудь объяснить, что здесь происходит?
1 ответ
В этой строке:
m.emplace(0, Test{ it });
... недавно вставленный Test
объект построен из std::forward<Test>(Test{ it })
, так что конструктор перемещения действительно вызывается (из-за переадресации копирование elision здесь не работает). Если вы хотите построить Test
напрямую, вы можете использовать m.emplace(0, it)
вместо.
Теперь мы можем видеть
С
Test(Test &&) = default;
, временный объект, обозначенныйTest{ it }.v
перемещается вm.at(0).v
, Еслиit
остается в силе ( это не гарантируется),m.at(0).v.end() == it
оцениваетtrue
; в противном случае программа приводит к неопределенному поведению.С
Test(Test &&) {}
,m.at(0).v
инициализируется значением, иit
признан недействительным как временный объектTest{ it }.v
уничтожен Программа приводит к неопределенному поведению.
В реализации libstdC++ конечные итераторы для разных unordered_map
имеют одинаковое значение (аналогично std::istream_iterator
), поэтому поведение GCC является разумным.