【发布时间】:2017-06-26 02:08:21
【问题描述】:
我有一个类 (A),其复制构造函数已删除 (=delete;)、移动构造函数和移动赋值声明和定义。该类拥有一个不能被释放两次的资源,因此有只能移动的限制。
我有另一个类 (B),它包含 A 的实例(不是指针)。我为 B 类编写了一个移动赋值运算符,它在其 A 类的成员上调用 std::move()。
一张地图包含许多 B 的实例。我创建了一个 B 的实例,并通过赋值直接分配它的 A 成员。然后我尝试将 B 插入到地图中。我以不同的方式尝试了operator[]、insert 和emplace,但我不断收到错误消息,声称我正在尝试使用签名为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;
}
}
}
【问题讨论】:
-
附注:管理资源有更简单的方法。它们可能需要更多代码(不多),但您不必忍受不可复制的对象。最简单的方法(也是需要更少代码的方法):将您的资源包装到
std::shared_ptr。它会为您解决一切问题。
标签: c++ visual-c++ move copy-constructor move-semantics