2

我有一个不寻常的情况,

假设我有一个像下面这样的课程,

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'soperator[]将返回一个引用一样,该类也必须通过返回一个引用来授予客户端完全访问权限。一个 setter/getter 对是不行的。

但是,与 不同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可以复制。问题的核心,是在类中看到的CC有几个成员,他们都有一个size属性,在构造的时候,它们都构造了相同的size,如果value_允许通过赋值替换,那么它允许数据结构被破坏,因为成员可能不再具有相同的size属性。

要求T仅在大小相同时允许复制或分配也不是一种选择。因为,在复制C对象时,源和目标之间的大小可能不同。例如,

C c1(10);
C c2(20);
c1 = c2;

完全合理。的大小c1变了,但它的所有成员也都改成了同样的新大小,所以没关系。

我希望我已经把问题说清楚了。我总结一下,我希望C对 没有太多限制TT基本上可以是任何类型,带有所需的构造函数。T可以复制和分配。我不希望客户做的唯一事情是分配到value_through C::value()

4

2 回答 2

3

如果您希望用户能够在对象上调用非常量成员函数并且想要返回对实际对象的引用,则不能完全禁止赋值,因为赋值运算符基本上就是这样(您可以重写a = b作为a.operator=(b))。因此,您要么只需要返回一个对您的对象的 const 引用,要么制作包含的对象,non_copyable要么接受它可以分配给它的事实。

我个人建议重新考虑设计。即使您可以禁止分配,也不能保证该对象没有成员函数,它的想法基本相同(.swap(...)是典型的候选者),所以只要您允许调用,您就没有真正赢得任何东西非常量成员函数。

但是,如果您只关心不允许意外分配,则可以使执行此类分配变得更加困难。如果你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 部分仍然可以分配给 using static_cast<T&>(C.value()) = foo;

要支持无法从(内置...)派生的类型,您需要创建一个代理,它公开除分配之外的所有功能。

于 2012-12-09T13:36:44.233 回答
1

至于你的 getter/setter 问题,我会写

const T& value() const; // as getter
void value(const T&); // as setter

返回const T&(const-reference) 完全反对类似的情况c.value() = 10(参见 Scott Meyers 的 Effective C++,第 23 条)。

我认为这也解决了复制问题:您的课程仍然是可复制的。

于 2012-12-09T13:34:17.903 回答