我最近偶然发现了move构造器的一些奇怪行为(从我的角度来看很奇怪)。使用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;
}
因此,我在创建元素时将迭代器存储到map元素中map的末尾。我也参考它以供以后比较。来自std::unordered_map::emplace:
将新元素插入到容器中,该容器使用 如果容器中没有带键的元素,则给出args。
谨慎使用emplace可以在构建新元素的同时 避免不必要的复制或移动操作。
使用默认的move构造函数,存储在map元素中的迭代器与我的引用相同:
Test(Test &&) = default;
Results在GCC中是true
,在VS中是true
。现在,如果我将move构造函数更改为:
Test(Test &&) {}
GCC still returns true
,但VS返回false
以防万一尝试使用c ++ 17,结果相同。所以有人可以解释一下这里发生了什么吗?
答案 0 :(得分:4)
在这一行:
m.emplace(0, Test{ it });
...新插入的Test
对象是根据std::forward<Test>(Test{ it })
构造的,因此确实调用了move构造函数(由于转发,复制省略在这里不起作用)。如果您想直接构造Test
,则可以改用m.emplace(0, it)
。
现在我们可以看到
使用Test(Test &&) = default;
,将Test{ it }.v
指定的临时对象移动到m.at(0).v
中。如果it
仍然有效(this is not guaranteed),则m.at(0).v.end() == it
的值为true
;否则,程序将导致未定义的行为。
使用Test(Test &&) {}
,m.at(0).v
被值初始化,并且it
无效,因为Test{ it }.v
指定的临时对象被破坏。该程序导致未定义的行为。
在libstdc ++的实现中,不同unordered_map
的最终迭代器具有相同的值(类似于std::istream_iterator
),因此GCC的行为是合理的。