![](/img/trans.png)
[英]Writing containers that can handle implicit sharing, but turn it off for non-copyable types (like unique_ptr)?
[英]Writing a copyable unique_ptr for copyable classes with PIMPL idiom
我想编写一个使用 PIMPL 成语的类,但可以实现以下功能:
int main() {
MyClass myclass {12};
std::string val = myclass.get_value();
std::cout << val << "\n";
MyClass copy = myclass;
val = copy.get_value();
return 0;
}
也就是说,在使用 PIMPL 时提供复制功能。 使用std::unique_ptr
实现 PIMPL 会导致预期的编译错误:
error C2280: 'MyClass::MyClass(const MyClass &)': attempting to reference a deleted function
这是我的尝试:
类.hpp
#pragma once
#include <string>
#include "copyable_unique_ptr.hpp"
class MyClass {
private:
struct MyClassPrivate;
copyable_unique_ptr<MyClassPrivate> _impl;
public:
MyClass(int value);
~MyClass();
std::string get_value();
};
类.cpp
#include "class.hpp"
#include <string>
struct MyClass::MyClassPrivate {
int value;
};
MyClass::MyClass(int value)
: _impl(std::make_unique<MyClassPrivate>()) {
_impl->value = value;
}
MyClass::~MyClass() = default;
std::string MyClass::get_value() {
return std::to_string(_impl->value);
}
copyable_unique_ptr.hpp
#pragma once
#include <memory>
template <typename T>
class copyable_unique_ptr {
private:
std::unique_ptr<T> _ptr;
public:
copyable_unique_ptr(std::unique_ptr<T> &&ptr)
: _ptr(std::move(ptr)) {}
~copyable_unique_ptr() = default;
copyable_unique_ptr(const copyable_unique_ptr<T> &other) {
_ptr = std::make_unique<T>(*other._ptr);
}
copyable_unique_ptr(copyable_unique_ptr<T> &&other) {
_ptr = std::move(other._ptr);
}
copyable_unique_ptr &operator=(const copyable_unique_ptr<T> &other) {
_ptr = std::make_unique<T>(*other._ptr);
}
copyable_unique_ptr &operator=(copyable_unique_ptr<T> &&other) {
_ptr = std::move(other._ptr);
}
T *operator->() {
return _ptr.get();
}
};
这会导致以下错误: warning C4150: deletion of pointer to incomplete type 'MyClass::MyClassPrivate'; no destructor called
warning C4150: deletion of pointer to incomplete type 'MyClass::MyClassPrivate'; no destructor called
我在第一次尝试使用 unique_ptr 时也遇到了这个错误,解决方案是将析构函数移动到 class.cpp 中: MyClass::~MyClass() = default;
.
在使用copyable_unique_ptr
方法时,我真的不知道如何解决这个问题。
我在 SO 上看到了这个问题。 他们试图和我做同样的事情,但对答案的最后一条评论表明他们遇到了完全相同的问题。
虽然你可以在这里编写自定义智能指针,但我总是喜欢尽可能多地重用代码。 似乎您想要一个动态创建的对象,它具有复制语义而不是指针语义。 出于这个原因,我会毫不犹豫地将您的copyable_unique_ptr
称为指针。 我确实为此编写了一个类,并决定使用dynamic_object<T>
类的东西。
我会使用只包含一个元素的std::vector
来代替指针。 有点像这样:
class MyClass {
private:
struct MyClassPrivate;
std::vector<MyClassPrivate> _impl;
public:
MyClass(int value);
~MyClass();
std::string get_value();
};
struct MyClass::MyClassPrivate {
int value;
};
MyClass::MyClass(int value)
: _impl(1) {
_impl.front().value = value;
}
MyClass::~MyClass() = default;
std::string MyClass::get_value() {
return std::to_string(_impl.front().value);
}
但是,如果您真的想创建您的copyable_unique_ptr
我仍然在这里使用std::vector
来简化其编码并重用高质量的标准库代码:
也许有点像这样:
template<typename T>
class copyable_unique_ptr
: private std::vector<T>
{
using base_type = std::vector<T>;
public:
copyable_unique_ptr(std::unique_ptr<T>&& ptr)
: base_type(std::move(*ptr), 1) {}
copyable_unique_ptr(std::unique_ptr<T> const& ptr)
: base_type(*ptr, 1) {}
T& operator*() { return base_type::back(); }
T* operator->() { return base_type::data(); }
T const& operator*() const { return base_type::back(); }
T const* operator->() const { return base_type::data(); }
};
Sam Varshavchik 在评论中给出了一个提示,将复制构造函数和赋值运算符移到实现中修复了我试图编写的代码,这意味着:
类.hpp
#pragma once
#include <string>
#include "copyable_unique_ptr.hpp"
class MyClass {
private:
struct MyClassPrivate;
copyable_unique_ptr<MyClassPrivate> _impl;
public:
MyClass(int value);
MyClass(const MyClass &);
MyClass(MyClass &&);
MyClass &operator=(const MyClass &);
MyClass &operator=(MyClass &&);
~MyClass();
std::string get_value();
};
类.cpp
#include "class.hpp"
#include <string>
#include <iostream>
struct MyClass::MyClassPrivate {
int value;
};
MyClass::MyClass(int value)
: _impl(std::make_unique<MyClassPrivate>()) {
_impl->value = value;
}
MyClass::MyClass(const MyClass &) = default;
MyClass::MyClass(MyClass &&) = default;
MyClass &MyClass::operator=(const MyClass &) = default;
MyClass &MyClass::operator=(MyClass &&) = default;
MyClass::~MyClass() = default;
std::string MyClass::get_value() {
std::cout << &_impl << "\n";
return std::to_string(_impl->value);
}
它按预期工作。
另一方面,Galik 给出了一个答案,它提供了一个更好、更简单的copyable_unique_ptr
替代方案。 我更喜欢用他的方式。
坚持使用普通的unique_ptr
并在您的 cpp 文件中实现MyClass
复制可能更简单:
class MyClass {
private:
struct MyClassPrivate;
std::unique_ptr<MyClassPrivate> _impl;
public:
MyClass(int value);
MyClass(MyClass&&);
MyClass(const MyClass&);
~MyClass();
MyClass& operator = (const MyClass&);
MyClass& operator = (MyClass&&);
std::string get_value();
};
MyClass::MyClass(const MyClass& other)
: _impl(std::make_unique<MyClassPrivate>(*other._impl)) {
}
MyClass& MyClass::operator = (const MyClass& other) {
_impl = std::make_unique<MyClassPrivate>(*other._impl);
return *this;
}
MyClass::~MyClass() = default;
MyClass::MyClass(MyClass&&) = default;
MyClass& MyClass::operator = (MyClass&&) = default;
此代码(和您的原始代码)存在一个小问题,因为从对象移动的对象将具有 null _impl
您可能希望添加对 null 的检查,或者您可以替换为默认构造的对象而不是空指针,但是在大多数情况下可能是一种浪费?
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.