[英]vector<map<move-only type>> does not compile with MSVC
制作仅移动类型的 map 的向量在 Windows 上似乎无法正常工作。 在此处查看代码: https://godbolt.org/z/yAHmzh
#include <vector>
#include <map>
#include <memory>
// vector<vector<move-only>> works
void foo() {
std::vector<std::vector<std::unique_ptr<int>>> outer;
std::vector<std::unique_ptr<int>> inner;
std::unique_ptr<int> p = std::make_unique<int>(1);
inner.push_back(std::move(p));
outer.push_back(std::move(inner));
}
// vector<map<move-only>> fails to compile upon inserting an element.
void bar() {
std::vector<std::map<std::unique_ptr<int>, std::unique_ptr<int>>> vec;
std::map<std::unique_ptr<int>, std::unique_ptr<int>> map;
std::unique_ptr<int> p1 = std::make_unique<int>(1);
std::unique_ptr<int> p2 = std::make_unique<int>(2);
map.insert(std::make_pair(std::move(p1), std::move(p2)));
// The following line fails to compile on windows. It errors with a message about
// the unique_ptr copy constructor being explicitly deleted. This seems to only happen
// on windows. GCC and clang have no problem with this.
vec.push_back(std::move(map));
}
int main(int argv, char** argc)
{
foo();
bar();
}
GCC 和 Clang 对该代码没有问题,但 MSVC 无法编译。
寻找一种解决方法让我这样做,这将在所有主要编译器上编译。
为了对向量强制执行移动语义,我们需要使用noexcept
通知 C++(特别是std::vector )移动构造函数和析构函数不会抛出。 然后当向量增长时将调用移动构造函数。 请参阅此注释:
为了使强异常保证成为可能,用户定义的移动构造函数不应抛出异常。 例如,当元素需要重新定位时,std::vector 依赖于 std::move_if_noexcept 在移动和复制之间进行选择。
有关标准中所说内容的更多信息,请阅读C++ 移动语义和异常
如果构造函数不是noexcept ,则std::vector不能使用它,因为它不能确保标准要求的异常保证。
对于std::map ,该标准没有说明 map 的移动构造函数的异常安全性。 因此,编译器(在您的情况下为gcc
和clang
)可以将函数标记为 noexcept ,与标准是否强制要求无关。
有关替代方案或解决方法,请参阅下面的示例(使用gcc
测试):
#include <vector>
#include <map>
#include <memory>
void foo(void)
{
std::vector<std::vector<std::unique_ptr<int>>> outer;
std::vector<std::unique_ptr<int>> inner;
std::unique_ptr<int> p = std::make_unique<int>(1);
inner.emplace_back(std::move(p));
outer.emplace_back(std::move(inner));
}
void bar(void)
{
std::vector<std::pair<std::unique_ptr<int>, std::unique_ptr<int>>> vec;
std::unique_ptr<int> p1 = std::make_unique<int>(1);
std::unique_ptr<int> p2 = std::make_unique<int>(2);
auto pair = std::make_pair(std::move(p1), std::move(p2));
vec.emplace_back(std::move(pair));
}
void bar2(void)
{
std::vector<std::unique_ptr<std::map<std::unique_ptr<int>, std::unique_ptr<int>>>> vec;
std::unique_ptr<int> p1 = std::make_unique<int>(1);
std::unique_ptr<int> p2 = std::make_unique<int>(2);
auto map = std::make_unique<std::map<std::unique_ptr<int>, std::unique_ptr<int>>>();
map->emplace(std::move(p1), std::move(p2));
vec.emplace_back(std::move(map));
}
int main(int argc, char *argv[])
{
foo();
bar();
return 0;
}
奖金:
尽可能使用emplace_back 。 它可以更快(但通常不是),可以更清晰、更紧凑,但也存在一些缺陷(尤其是非显式构造函数)。
std::map
的移动构造函数没有被标准定义为noexcept
。 因此std::vector
回退到使用复制构造函数(例如通过使用std::move_if_noexcept
)。
也就是说,允许编译器将函数标记为noexcept
,与标准是否强制要求无关。 这可能是 GCC 和 Clang 所做的(他们使用的库)。
您会注意到相同的情况适用于std::vector<std::list<std::unique_ptr<int>>>
(也许还有其他)。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.