我有一个不寻常的情况,
假设我有一堂像下面的课,
template <typename T>
class C
{
public :
C (int size) : value_(size), some_other_member_(size) {}
T &value () {return value_;}
const T &value() const {return value_;}
private :
T value_;
SomeOtherType some_other_member_;
};
该类的设计使得客户端可以完全访问成员value_
,就像std::vector
的operator[]
会返回一个引用一样,这个类也必须通过返回一个引用来赋予客户端完全访问权限。二传手/吸气手对是不行的。
但是,与std::vector
不同,我不想让客户端能够完全替换成员。也就是说,客户应能够呼叫value_
的const
或非const
成员,但不允许以下情况:
C<SomeType> c(10);
SomeType another_value(5);
c.value() = another_value; // This shall not be allowed
有什么可能的方法可以让客户完全访问value_
。从某种意义上说,类C
应该像一个容器,但是一旦它包含的东西被初始化(通过构造函数,有T
的要求,但这在这里无关紧要),客户端只能通过T
的成员函数修改value_
,而不能通过赋值替换value_
。
但是,要求T
不可复制对我来说不是一个选择。因为类C
是可以复制的。问题的核心是,如类C
所示,C
有几个成员,它们都有一个size
属性,当构造时,它们都是用相同的size
构造的,如果允许通过赋值替换value_
,那么它允许数据结构被破坏,因为成员可能不再具有相同的size
属性。
要求T
仅在大小相同时才允许复制或分配也不是一种选择。因为,在复制C
对象时,源对象和目标对象之间的大小可能不同。例如
C c1(10);
C c2(20);
c1 = c2;
是完全合理的。c1
的大小已更改,但其所有成员也更改为相同的新大小,因此可以。
我希望我已经把问题说清楚了。我总结,我希望C
对T
没有太大的限制,T
基本上可以是任何具有所需构造函数的类型。 可以复制和分配T
。我不希望客户做的唯一瘦是分配给value_
通过C::value()
。
如果您希望用户能够在对象上调用非 const 成员函数,并且想要返回对实际对象的引用,则不能完全禁止赋值,因为赋值运算符基本上就是这样(您可以将a = b
重写为a.operator=(b)
)。因此,您要么只需要返回对对象的 const 引用,要么使包含的对象non_copyable
,要么接受可以分配给它的事实。
就个人而言,我建议重新考虑设计。即使你可以禁止赋值,也不能保证对象没有成员函数,它做的基本思考(.swap(...)
是一个典型的候选者),所以只要你允许调用非 const 成员函数,你就不会真正赢得任何东西。
但是,如果您只关心不允许意外分配,则可以使此类分配更加困难。如果T
不是内置的,则可以创建一个派生类,该类不会公开公共赋值运算符并返回对该运算符的引用:
template <typename T>
class C{
public :
class Derived: public T {
private:
Derived(int size):T(size) {}
Derived& operator=(const Derived&) = default; //used C++11 syntax for brevity, for C++03 code it has to be implemented here
Derived(const Derived&) = default; //don't want this class to be copyied outside of C
~Derived() = default;
friend class C;
};
C (int size) : value_(size), some_other_member_(size) {}
Derived& value () {return value_;}
const Derived& value() const {return value_;}
private :
Derived value_;
SomeOtherType some_other_member_;
};
这将通过继承为所有公共成员提供访问权限,但隐藏赋值运算符(和构造函数)。当然,如果您使用 c++11,则可以通过使用/定义移动构造函数/赋值和使用完美转发来增强此代码,以允许不同的构造函数。注意,派生的 T 部分仍然可以分配给使用static_cast<T&>(C.value()) = foo
;
为了支持无法派生的类型(内置...),您需要创建一个代理,该代理公开除赋值之外的所有功能。
至于你的getter/setter问题,我会写
const T& value() const; // as getter
void value(const T&); // as setter
返回const T&
(const-reference)完全反对c.value() = 10
等情况(参见例如。斯科特·迈耶斯的有效C++,第23项)。
我认为这也解决了复制问题:你的类仍然是可复制的。