我有一个不寻常的情况,
说我有类似以下的课程,
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
的{{1}}将返回引用一样,此类也必须通过返回引用为客户端提供完全访问权限。 setter / getter对不会这样做。
但是,与operator[]
不同,我不希望客户端能够完全替换成员。也就是说,客户应该可以致电std::vector
成员const
或const
成员,但不允许以下内容,
value_
我是否可以通过任何方式向客户授予对 C<SomeType> c(10);
SomeType another_value(5);
c.value() = another_value; // This shall not be allowed
的完全访问权限。在某种意义上,类value_
应该像一个容器,但是一旦它包含的东西被初始化(通过构造函数,有C
的要求,但这里没有相关性),客户端可以只修改T
到value_
的成员函数,而不是通过赋值替换T
。
但是,要求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_
。
答案 0 :(得分:3)
如果希望用户能够在对象上调用非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,这个代码可以通过使用/定义移动构造函数/赋值和使用完美转发来增强,以允许不同的构造函数。请注意,Derived的T部分仍然可以使用static_cast<T&>(C.value()) = foo
;
要支持您无法从(builtins ...)派生的类型,您需要创建一个代理,它会公开除分配之外的所有功能。
答案 1 :(得分:1)
关于你的getter / setter问题,我会写
const T& value() const; // as getter
void value(const T&); // as setter
返回const T&
(const-reference)完全违反c.value() = 10
之类的情况(参见Scott Meyers的有效C ++,第23项)。
我认为这也解决了复制问题:您的课程仍然可以复制。