简体   繁体   English

向量<map<move-only type> &gt; 不能用 MSVC 编译</map<move-only>

[英]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.因此,编译器(在您的情况下为gccclang )可以将函数标记为 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.

相关问题 插入只移动类型的向量 - Inserting into a vector of move-only type 我可以列表初始化一个只移动类型的向量吗? - Can I list-initialize a vector of move-only type? MSVC将仅移动结构参数解释为指针 - MSVC interpreting move-only struct argument as a pointer 仅移动类型的back_inserter - back_inserter for move-only type 在从仅移动类型派生的 class 中定义析构函数会在使用 std::vector 的 emplace_back 或 push_back 创建时产生编译时错误 - defining destructor in a class derived from move-only type gives compile-time error when created with emplace_back or push_back of std::vector 仅移动类型返回到转换构造函数 - Move-only type returned into converting constructor 在仅移动类型上强制复制(然后销毁) - Force copy (then destroy) on move-only type std :: move-only类型的列表:无法在VC ++中进入std :: vector - std::list of move-only type: Cannot emplace into std::vector in VC++ 包含仅移动类型的类的构造函数是应该通过引用还是通过右值引用来接收仅移动类型? - Should a constructor for a class that contains a move-only type receive the move-only type by reference or by rvalue reference? 如何在Visual C ++中将仅移动对象插入到地图中? - How to insert a move-only object into a map in Visual C++?
 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM