Переместить поведение конструктора - PullRequest
0 голосов
/ 15 декабря 2018

Недавно я наткнулся на какое-то странное поведение (странное, с моей точки зрения) в конструкторе ходов.Результат отличается при компиляции с 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;
}

Поэтому я сохраняю итератор до конца карты в элементе карты при создании элемента.Я также беру ссылку на это, чтобы сравнить позже.From std :: unordered_map :: emplace :

Вставляет новый элемент в контейнер, созданный на месте с заданными аргументами, если в контейнере нет элемента с ключом.

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

Используя конструктор перемещения по умолчанию, итератор, сохраненный в элементе карты, и моя ссылкато же самое:

Test(Test &&) = default; 

Результаты равны true в GCC и true в VS.Теперь, если я изменяю конструктор перемещения на:

Test(Test &&) {} 

GCC по-прежнему возвращает true, но VS возвращает false

На всякий случай, пытаясь с c ++ 17, те же результаты.Так может кто-нибудь объяснить, что здесь происходит?

1 Ответ

0 голосов
/ 15 декабря 2018

В этой строке:

m.emplace(0, Test{ it });

... недавно вставленный объект Test создается из std::forward<Test>(Test{ it }), поэтому конструктор перемещения действительно вызывается (из-за пересылки, elision copy не делаетработать здесь).Если вы хотите построить 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разумно.

...