简体   繁体   English

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

[英]How to insert a move-only object into a map in Visual C++?

I have a class (A) with its copy constructors deleted ( =delete; ), move constructor and move assignment declared and defined. 我有一个类(A),它的副本构造函数已删除( =delete; ),移动了构造函数,并声明和定义了移动分配。 The class holds a resource that must not be freed twice, hence the move-only restriction. 该类拥有一个不能释放两次的资源,因此只能移动。

I have another class (B) that holds an instance of A (not a pointer). 我有另一个类(B),其中包含A的一个实例(不是指针)。 I've written a move assignment operator for class B which calls std::move() on its member of class A. 我已经为类B编写了一个移动分配运算符,它在类A的成员上调用std::move()

A map holds many instances of B. I create an instance of B and assign its A member directly with assignment. 映射包含B的许多实例。我创建了B的实例,并直接通过赋值分配其A成员。 Then I try to insert B into the map. 然后,我尝试将B插入地图。 I've tried operator[] , insert and emplace in different ways, but I keep getting an error claiming I'm attempting to use a deleted function with the signature A(const A&) . 我尝试用不同的方式尝试operator[]insertemplace ,但是我一直收到错误消息,声称我正在尝试使用带有签名A(const A&)的已删除函数。 So it's trying to use the copy constructor despite it being deleted? 因此,尽管它被删除,它仍尝试使用副本构造函数吗?

Everything is fine until I try to insert it into the map. 一切正常,直到我尝试将其插入地图。

I'm using VS2013. 我正在使用VS2013。

Edit: It's important for my project (probably unrelated to the problem in question) that the method I end up using does not perform any library interaction with the WinAPI (kernel32/ntdll etc.), so any methods calling operator new() or similar internally of any sort that delegates to the WinAPI I will be unable to use. 编辑:这是我的项目(可能是无关的问题的问题),我最终使用的方法执行与WinAPI的(KERNEL32 / NTDLL等)的任何图书馆的互动很重要,因此任何方法调用operator new()或类似内部将委托给WinAPI的任何形式的我都无法使用。 The map has been pre-allocated with reserve() to try to alleviate this. 该地图已预先分配了reserve()来缓解这种情况。

Code (the call to emplace is what causes the compilation error): 代码(对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;
        }
    }
}

You can use emplace but you need to use std::move in conjunction with it in order to cast the object you already have into an rvalue reference. 您可以使用emplace但需要与std::move结合使用,以便将您已有的对象转换为右值引用。 std::unique_ptr is only movable and you can put it into a map just like std::unique_ptr仅可移动,您可以将其放入地图中,就像

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

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM