如何在 Visual C++ 中将仅移动对象插入到地图中?

Mik*_*yte 3 c++ move copy-constructor visual-c++ move-semantics

我有一个类 (A),其复制构造函数被删除 ( =delete;),移动构造函数和移动赋值被声明和定义。该类持有的资源不得释放两次,因此存在仅移动限制。

我有另一个类 (B),它保存 A 的实例(不是指针)。我为 B 类编写了一个移动赋值运算符,它调用std::move()A 类的成员。

映射包含 B 的许多实例。我创建 B 的实例并直接通过赋值为其分配 A 成员。然后我尝试将 B 插入到地图中。我已经尝试过operator[]insertemplace以不同的方式,但我不断收到错误,声称我正在尝试使用带有签名的已删除函数A(const A&)。那么尽管它被删除了,它还是试图使用复制构造函数?

一切都很好,直到我尝试将其插入地图中。

我用的是VS2013。

编辑:对于我的项目来说很重要(可能与所讨论的问题无关),我最终使用的方法不会WinAPI (kernel32/ntdll 等)执行任何库交互,因此任何operator new()内部调用或类似的方法委托给 WinAPI 我将无法使用。该地图已预先分配reserve()以试图缓解这一问题。

代码(调用emplace是导致编译错误的原因):

/* main.cpp */

#include "smart_handle.h"
#include <unordered_map>
#include <Windows.h>

struct MetadataStruct
{
    MetadataStruct& operator=(MetadataStruct&& other)
    {
        if (this != &other)
        {
            handle = std::move(other.handle);
            // Invalidate other.handle somehow...
        }
        return *this;
    }

    Util::SmartHandle handle;
    // Also has other members.
};

std::unordered_map<DWORD, MetadataStruct> metadataStructs;

Util::SmartHandle GetHandle()
{
    // Doing lots of stuff here to get the handle, then finally wraps
    // it before returning, hence the need for its own function.
    const HANDLE openedFileHandle = (HANDLE)-1; // Just an example handle.
    return std::move(Util::SmartHandle(openedFileHandle, NULL));
}

void F()
{
    MetadataStruct metadataStruct;
    metadataStruct.handle = GetHandle();

    metadataStructs.emplace(0, std::move(metadataStruct));
}

int main(int argc, char** argv)
{
    F();
    return 0;
}

/* smart_handle.h */
#pragma once

#include <stdexcept>
#include <Windows.h>

namespace Util
{
    class SmartHandle
    {
    public:
        SmartHandle() = default;
        SmartHandle(HANDLE handle, HANDLE invalidHandleValue);

        SmartHandle(const SmartHandle& other) = delete;
        SmartHandle& operator=(const SmartHandle& other) = delete;

        SmartHandle(SmartHandle&& other);
        SmartHandle& operator=(SmartHandle&& other);

        ~SmartHandle();

        HANDLE GetValue() const;
        bool IsValid() const;
        void Close();
    private:
        static const HANDLE uninitializedHandleValue;

        HANDLE handle = uninitializedHandleValue;
        HANDLE invalidHandleValue = uninitializedHandleValue;

        void Set(HANDLE handle, HANDLE invalidHandleValue, bool throwIfInvalid = true);
    };
}

/* smart_handle.cpp */

#include "smart_handle.h"

namespace Util
{
    const HANDLE SmartHandle::uninitializedHandleValue = (HANDLE)-2;

    SmartHandle::SmartHandle(const HANDLE handle, const HANDLE invalidHandleValue)
    {
        Set(handle, invalidHandleValue);
    }

    SmartHandle::SmartHandle(SmartHandle&& other)
    {
        handle = other.handle;
        invalidHandleValue = other.invalidHandleValue;

        other.handle = uninitializedHandleValue;
        other.invalidHandleValue = uninitializedHandleValue;
    }

    SmartHandle& SmartHandle::operator=(SmartHandle&& other)
    {
        if (this != &other)
        {
            handle = other.handle;
            invalidHandleValue = other.invalidHandleValue;

            other.handle = uninitializedHandleValue;
            other.invalidHandleValue = uninitializedHandleValue;
        }
        return *this;
    }

    SmartHandle::~SmartHandle()
    {
        Close();
    }

    void SmartHandle::Set(const HANDLE handle, const HANDLE invalidHandleValue, const bool throwIfInvalid)
    {
        this->handle = handle;
        this->invalidHandleValue = invalidHandleValue;

        if (throwIfInvalid && !IsValid())
        {
            this->handle = uninitializedHandleValue;
            this->invalidHandleValue = uninitializedHandleValue;
            throw std::invalid_argument("The handle used to initialize the object is not a valid handle");
        }
    }

    HANDLE SmartHandle::GetValue() const
    {
        if (handle == uninitializedHandleValue)
        {
            throw std::exception("Handle value not initialized");
        }

        return handle;
    }

    bool SmartHandle::IsValid() const
    {
        return handle != uninitializedHandleValue && handle != invalidHandleValue;
    }

    void SmartHandle::Close()
    {
        const bool isPseudoHandle = handle == (HANDLE)-1;

        if (IsValid() && !isPseudoHandle)
        {
            if (!CloseHandle(handle))
            {
                throw std::exception("CloseHandle failed");
            }

            handle = invalidHandleValue;
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

Nat*_*ica 5

您可以使用emplace,但需要std::move与其结合使用,以便将已有的对象转换为右值引用。 std::unique_ptr只能移动,你可以将它放入地图中,就像

int main() {
    std::unique_ptr<int> foo;
    std::map<int, std::unique_ptr<int>> bar;
    bar.emplace(1, std::move(foo));
}
Run Code Online (Sandbox Code Playgroud)