[英]vector<map<move-only type>> does not compile with MSVC
Making a vector of a map of move-only types doesn't seem to work properly on Windows.制作仅移动类型的 map 的向量在 Windows 上似乎无法正常工作。 See code here: https://godbolt.org/z/yAHmzh在此处查看代码: 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 and Clang have no problem with that code, but MSVC fails to compile. GCC 和 Clang 对该代码没有问题,但 MSVC 无法编译。
Looking for a workaround to let me do this that will compile on all major compilers.寻找一种解决方法让我这样做,这将在所有主要编译器上编译。
To enforce move semantics for vector, we need to inform C++ (specifically std::vector ) that the move constructor and destructor does not throw, using noexcept
.为了对向量强制执行移动语义,我们需要使用noexcept
通知 C++(特别是std::vector )移动构造函数和析构函数不会抛出。 Then the move constructor will be called when the vector grows.然后当向量增长时将调用移动构造函数。 See this note:请参阅此注释:
To make the strong exception guarantee possible, user-defined move constructors should not throw exceptions.为了使强异常保证成为可能,用户定义的移动构造函数不应抛出异常。 For example, std::vector relies on std::move_if_noexcept to choose between move and copy when the elements need to be relocated.例如,当元素需要重新定位时,std::vector 依赖于 std::move_if_noexcept 在移动和复制之间进行选择。
For more about what's said in the standard, read C++ Move semantics and Exceptions有关标准中所说内容的更多信息,请阅读C++ 移动语义和异常
If the constructor is not noexcept , std::vector can't use it, since then it can't ensure the exception guarantees demanded by the standard.如果构造函数不是noexcept ,则std::vector不能使用它,因为它不能确保标准要求的异常保证。
In case of std::map , the standard doesn't say anything about exception safety for move constructor of the map.对于std::map ,该标准没有说明 map 的移动构造函数的异常安全性。 So, compilers (in your case, gcc
and clang
) can mark functions as noexcept irrelevant of whether the Standard mandates it or not.因此,编译器(在您的情况下为gcc
和clang
)可以将函数标记为 noexcept ,与标准是否强制要求无关。
For alternatives or workaround, see my example below (tested with gcc
):有关替代方案或解决方法,请参阅下面的示例(使用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;
}
BONUS:奖金:
Use emplace_back when possible.尽可能使用emplace_back 。 It can be faster (but often is not), it can be clearer and more compact, but there are also some pitfalls (especially with non-explicit constructors).它可以更快(但通常不是),可以更清晰、更紧凑,但也存在一些缺陷(尤其是非显式构造函数)。
The move constructor of std::map
is not defined to be noexcept
by the Standard. std::map
的移动构造函数没有被标准定义为noexcept
。 Therefore std::vector
falls back to using the copy constructor (eg by the use of std::move_if_noexcept
).因此std::vector
回退到使用复制构造函数(例如通过使用std::move_if_noexcept
)。
That said, compilers are allowed to mark functions as noexcept
irrelevant of whether the Standard mandates it or not.也就是说,允许编译器将函数标记为noexcept
,与标准是否强制要求无关。 This is probably what GCC and Clang do (the libraries that they use).这可能是 GCC 和 Clang 所做的(他们使用的库)。
You'll notice the same situation applies for std::vector<std::list<std::unique_ptr<int>>>
(and perhaps other).您会注意到相同的情况适用于std::vector<std::list<std::unique_ptr<int>>>
(也许还有其他)。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.