如何创建模板使现有类实例不可拷贝不可赋值(不继承原类)
实现方案
核心思路是通过组合而非继承持有原类实例,显式删除模板类的拷贝构造函数与拷贝赋值运算符,同时提供访问底层实例的接口,确保既能正常使用原类功能,又能杜绝意外拷贝。
模板实现代码
#include <utility> template <typename T> class NonCopyable { public: // 转发所有构造函数给底层T实例 template <typename... Args> explicit NonCopyable(Args&&... args) : m_instance(std::forward<Args>(args)...) {} // 允许移动构造和移动赋值(若需彻底禁止移动可改为= delete) NonCopyable(NonCopyable&&) noexcept = default; NonCopyable& operator=(NonCopyable&&) noexcept = default; // 显式删除拷贝构造与拷贝赋值,禁止拷贝 NonCopyable(const NonCopyable&) = delete; NonCopyable& operator=(const NonCopyable&) = delete; // 提供访问底层实例的接口 T& get() noexcept { return m_instance; } const T& get() const noexcept { return m_instance; } // 重载->和*运算符,方便直接调用底层实例的成员 T* operator->() noexcept { return &m_instance; } const T* operator->() const noexcept { return &m_instance; } T& operator*() noexcept { return m_instance; } const T& operator*() const noexcept { return m_instance; } private: T m_instance; // 组合持有原类实例 };
代码说明
- 构造函数转发:使用可变参数模板和完美转发,兼容原类
T的所有构造方式(包括列表初始化),比如NonCopyable<std::vector<int>> vec{1,2,3}可以正常工作。 - 删除拷贝操作:显式声明
= delete拷贝构造和拷贝赋值运算符,任何尝试拷贝NonCopyable<T>实例的代码都会触发编译错误,完全符合需求。 - 移动操作:默认保留移动构造和移动赋值,允许实例的资源转移(不需要的话可改为
= delete)。 - 访问接口:通过
get()方法、operator->和operator*,可以像直接使用原类实例一样调用其成员,比如vec->push_back(4)或者(*vec).size()。
验证示例
#include <vector> #include <iostream> int main() { NonCopyable<std::vector<int>> vec1{1, 2, 3}; std::cout << vec1->size() << std::endl; // 输出3,正常使用原类功能 // NonCopyable<std::vector<int>> vec2(vec1); // 编译错误:拷贝构造被删除 // vec2 = vec1; // 编译错误:拷贝赋值被删除 // 移动操作是允许的(若保留了默认移动) NonCopyable<std::vector<int>> vec3(std::move(vec1)); std::cout << vec3->size() << std::endl; // 输出3 return 0; }
补充说明
如果需要彻底禁止所有拷贝和移动,可以将移动构造和移动赋值也声明为= delete,但通常保留移动操作更符合C++的资源管理习惯。这个模板完全满足“不继承原类、仅持有实例、禁止意外拷贝”的要求,从编译层面杜绝错误操作。
内容的提问来源于stack exchange,提问作者Jörg Brüggmann
相关产品推荐
相关产品推荐

