移动构造函数行为

Kil*_*Kid 7 c++ move-constructor

我最近从移动构造函数中偶然发现了一些奇怪的行为(从我的观点来看很奇怪).使用GCC和Visual Studio进行编译时,结果会有所不同.我想听听这种行为的解释,不要认为这是一个bug,但可能是特定于编译器的.

请考虑以下代码:

#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;
}
Run Code Online (Sandbox Code Playgroud)

所以我在创建元素时将迭代器存储到map元素中的map的末尾.我也参考了它以便稍后进行比较.来自std :: unordered_map :: emplace:

如果容器中没有带键的元素,则使用给定的args将新元素插入到就地构造的容器中.

小心使用emplace可以构建新元素,同时避免不必要的复制或移动操作.

使用默认移动构造函数,存储在map元素中的迭代器和我的引用是相同的:

Test(Test &&) = default; 
Run Code Online (Sandbox Code Playgroud)

结果true在GCC和true在VS. 现在,如果我将移动构造函数更改为:

Test(Test &&) {} 
Run Code Online (Sandbox Code Playgroud)

海湾合作委员会仍然返回 true但VS返回false

以防万一尝试使用c ++ 17,结果相同.那么有人可以解释一下这里发生了什么吗?

xsk*_*xzr 4

在这一行中:

m.emplace(0, Test{ it });
Run Code Online (Sandbox Code Playgroud)

...新插入的Test对象是从 构造的std::forward<Test>(Test{ it }),因此确实调用了移动构造函数(由于转发,复制省略在这里不起作用)。如果想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_maps的结束迭代器具有相同的值(类似于std::istream_iterator),因此GCC的行为是合理的。