Переместить поведение конструктора

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

Другие вопросы по тегам