带额外参数的RAII类拷贝与移动赋值运算符实现方案咨询
RAII类拷贝/赋值需额外参数的解决方案
问题场景
我有一个管理资源的RAII类,拷贝该资源需要一个与资源本身无关的额外参数,仅作为拷贝操作的入参。为此我给类定义了带额外参数(带默认值)的拷贝构造函数,但拷贝赋值与移动赋值运算符不允许携带额外参数;如果把该参数设为类成员,又容易让用户误用——因为每次调用拷贝/移动赋值前都要手动设置它。
代码示例:
struct Foo{ void* ptr; Foo(size_t){/*allocate*/} ~Foo(){/*deallocate*/} Foo(const Foo& that, int x = 0 /*required additional parameter for copying*/){CopyMethod(*this,that,x);} Foo& operator=(Foo);// cannot implement without int x parameter };
可行解决思路
禁用默认赋值,提供带参数的赋值成员函数
直接删除默认的赋值运算符,转而定义带额外参数的成员函数完成赋值,结合copy-swap惯用法保证异常安全。用户必须显式传入参数,从根源避免误用:struct Foo{ void* ptr; Foo(size_t){/*allocate*/} ~Foo(){/*deallocate*/} Foo(const Foo& that, int x = 0){CopyMethod(*this,that,x);} // 禁用默认赋值运算符 Foo& operator=(const Foo&) = delete; Foo& operator=(Foo&&) = delete; // 带参数的拷贝赋值,copy-swap实现 Foo& assign(const Foo& that, int x) { Foo temp(that, x); swap(*this, temp); return *this; } // 带参数的移动赋值(按需实现) Foo& assign(Foo&& that, int x) { Foo temp(std::move(that), x); swap(*this, temp); return *this; } // 友元swap函数,实现资源交换 friend void swap(Foo& lhs, Foo& rhs) noexcept { using std::swap; swap(lhs.ptr, rhs.ptr); } };调用示例:
foo.assign(other_foo, 5);,清晰直观且强制参数传入。用辅助类包装赋值参数
定义辅助类持有拷贝所需的额外参数,让operator=接受该辅助类对象,既保留赋值语法,又强制用户传入参数:struct Foo{ void* ptr; Foo(size_t){/*allocate*/} ~Foo(){/*deallocate*/} Foo(const Foo& that, int x = 0){CopyMethod(*this,that,x);} // 拷贝用辅助类 struct CopyWith { const Foo& source; int param; CopyWith(const Foo& src, int x) : source(src), param(x) {} }; // 移动用辅助类 struct MoveWith { Foo&& source; int param; MoveWith(Foo&& src, int x) : source(std::move(src)), param(x) {} }; // 接受拷贝辅助类的赋值运算符 Foo& operator=(CopyWith cp) { Foo temp(cp.source, cp.param); swap(*this, temp); return *this; } // 接受移动辅助类的赋值运算符 Foo& operator=(MoveWith mv) { Foo temp(std::move(mv.source), mv.param); swap(*this, temp); return *this; } friend void swap(Foo& lhs, Foo& rhs) noexcept { using std::swap; swap(lhs.ptr, rhs.ptr); } };调用示例:
foo = Foo::CopyWith(other_foo, 5);,用语法形式约束参数传入。类成员参数加安全校验(不推荐,仅适配特殊场景)
若必须保留默认赋值运算符,将参数设为类成员,但添加校验机制防止未设置参数就调用赋值:#include <stdexcept> struct Foo{ void* ptr; int copy_param = -1; bool param_is_set = false; Foo(size_t){/*allocate*/} ~Foo(){/*deallocate*/} Foo(const Foo& that, int x = 0){CopyMethod(*this,that,x);} // 设置拷贝参数并标记状态 void set_copy_param(int x) { copy_param = x; param_is_set = true; } Foo& operator=(const Foo& that) { if (!param_is_set) { throw std::runtime_error("Copy parameter not set before assignment"); } Foo temp(that, copy_param); swap(*this, temp); param_is_set = false; // 使用后重置,避免重复误用 return *this; } friend void swap(Foo& lhs, Foo& rhs) noexcept { using std::swap; swap(lhs.ptr, rhs.ptr); swap(lhs.copy_param, rhs.copy_param); swap(lhs.param_is_set, rhs.param_is_set); } };这种方式依赖用户主动设置参数,若用户遗忘会触发异常,复杂度较高,仅适合必须兼容默认赋值语法的场景。
内容的提问来源于stack exchange,提问作者Tyson Hilmer
相关产品推荐
相关产品推荐

