单个类,它结合了const和非const引用数据成员

时间:2012-02-21 13:59:40

标签: c++ const-correctness

这是一组实现一种适配器模式的C ++类:

#include <iostream>

class Cfoo
{
public:
    explicit Cfoo(int i):i_(i){}
    void SetI(int i){ i_ = i; }
    int GetI()const{ return(i_); }
private:
    int i_;
};

class CfooHolderConst
{
public:
    explicit  CfooHolderConst(const Cfoo& foo):foo_(foo){}
    int GetI()const{ return( foo_.GetI() ); }
private:
    const Cfoo& foo_;
};

class CfooHolderNonConst
{
public:
    explicit CfooHolderNonConst(Cfoo& foo):foo_(foo){};
    int GetI()const{ return( foo_.GetI() ); }
    void SetI(int i){ foo_.SetI(i); }
private:
    Cfoo& foo_;
};

int main(  int argc, char* argv[] )
{
    const Cfoo myConstFoo(42);
    CfooHolderConst myConstFooHolder(myConstFoo);
    std::cout << myConstFooHolder.GetI() << std::endl;

    Cfoo myNonConstFoo(1);
    CfooHolderNonConst myNonConstFooHolder(myNonConstFoo);
    myNonConstFooHolder.SetI(42);
    std::cout << myConstFooHolder.GetI() << std::endl;

    return(0);
}

我想将CfooHolderNonConst和CFooHolderConst组合成一个单独的类,或者失败,继承另一个类。对Cfoo的引用在这里是一个问题,因为在CFooHolderConst中它需要定义为const Cfoo&amp;,而在CfooHolderNonConst中它需要是Cfoo&amp;。

这是与interator / const_iterator类似的问题: How to avoid code duplication implementing const and non-const iterators?

...但我希望,因为这不必满足STL迭代器的要求,可能会有一个更简单的解决方案。

过去我通过将const和nonconst指针都作为类成员,并从重载的构造函数中设置一个或另一个来解决这类问题。这浪费了空间,看起来很笨拙。有更优雅的解决方案吗?

5 个答案:

答案 0 :(得分:1)

您可以创建一个模板类,它将为该类的const和非const版本提供const功能,然后继承以扩展非const版本,并具有修改该成员的功能:

class Cfoo
{
public:
    explicit Cfoo(int i):i_(i){}
    void SetI(int i){ i_ = i; }
    int GetI()const{ return(i_); }
private:
    int i_;
};

class CfooHolderNonConst;

template<class Foo>
class CFooHolder
{
    friend class CfooHolderNonConst;
public:
    explicit  CFooHolder(Foo& foo):foo_(foo){}
    int GetI()const{ return( foo_.GetI() ); }
private:
    Foo& foo_;
};

typedef CFooHolder<const Cfoo> CfooHolderConst;

class CfooHolderNonConst: public CFooHolder<Cfoo>
{
public:
    explicit CfooHolderNonConst(Cfoo& foo):CFooHolder(foo){};
    void SetI(int i){ foo_.SetI(i); }
};

答案 1 :(得分:1)

是的,可以做到:

template< typename T > CHolderReader
{
 public: 
    explicit  CHolderBase( T& t):t_(t){}
    int Get()const { return t_.GetI(); }

 protected:
    ~CHolderReader() {}

 protected:
    T& t_;
};

template< typename T > CHolderReaderWriter : public CHolderReader< T >
{
public:
   void Set( int i)
   {
       t_.SetI(i);
   }
};

typedef CHolderReader<const Cfoo> CFooHolderConst;
typedef CHolderReaderWriter<Cfoo> CFooHolderNonConst;    

实际上,这是一个将底层数据的获取包装在const或非const状态的示例。除非模板化类型是const,否则读者持有非const引用,但不允许您写入它,因此当您需要写入时,可以像使用CHolderReaderWriter一样扩展它。

答案 2 :(得分:0)

我认为将const-和非const-接口都作为一个单独的类是个好主意。

它们为用户提供不同的接口,并具有不同的语义。在您的示例中,复制也很少。

答案 3 :(得分:0)

如果你真的想拥有相同的类(提供语义仍然有意义),那么我想你想要的东西:

const Cfoo f1( 5 );
const CfooHolder h1( f1 );

Cfoo f2( 0 );
CfooHolder h2( f2 );

我认为你希望C ++做出以下决定: a)将 Cfoo 对象视为const,如果它是const,或者将非const视为非常量。线索既是 Cfoo 的定义,也是 CfooHolder 的定义。如果 Cfoo const,则 CfooHolder 必须声明为const,否则无法编译。如果 Cfoo 是非常量的,那么您可以创建 CfooHolder ,这可以是const和非 - const。 b)当在const CfooHolder对象中使用时,方法SetI()应该停止编译。在上面的示例中,h1.SetI( 6 );不应该编译。

我的回答是,如果a)工作,那么b)也会自动工作。问题是实现a),据我所知,这是不可能的。

为了使其正常工作,该属性应该在其类的对象的const或非const的环境下成为const或非const。虽然该类的对象可以更改此“状态”,但属性保持不变。但是,当此类的对象为const时,您只能使用const方法(例如,通过常量引用传递参数时)。所以,C ++不会支持它,因为它不会那样工作。

另一种可能性是让属性本身同时为const和非const,这没有意义。

简短的回答是:它无法完成,并且会有代码重复。如果你真的想要避免这种情况,并且包装器足够复杂而不必担心,唯一的方法是创建一个通用的持有者,然后围绕一般持有者的常量和非常量包装器,避免重复到最小的。< / p>

class CfooHolder
{
public:
    explicit CfooHolder(Cfoo& foo):foo_(foo){};
    int GetI()const{ return( foo_.GetI() ); }
    virtual void SetI(int i){ foo_.SetI(i); }
protected:
    Cfoo& foo_;
};

class CfooHolderNonConst : public CfooHolder {
public:
    explicit CfooHolderNonConst(Cfoo& foo):CfooHolder(foo){};
};

class CfooHolderConst: public CfooHolder
{
public:
    explicit  CfooHolderConst(const Cfoo& foo):CfooHolder(const_cast<Cfoo &>( foo )){}
    void SetI(int i){ throw std::runtime_error( "Don't write to me!" ); }
};

它并不完美,但它符合所述条款。方法SetI()会抛出运行时错误,但如果 CfooHolderConst 对象声明为const,则对SetI()的调用甚至不会编译。< / p>

希望这有帮助。

答案 4 :(得分:0)

是否有一个特定原因导致您不能仅将FooHolder用于非const(可变)访问,而const FooHolder用于const访问?

你不能在const对象上调用非const限定的方法(如SetI),所以看起来它就像你想要的那样。显然,你最初需要从非const Cfoo 创建持有者对象。

示例:

class Cfoo
{
public:
    explicit Cfoo(int i) : i_(i) {}
    void SetI(int i) { i_ = i; }
    int GetI() const { return(i_); }
private:
    int i_;
};

class CfooHolder
{
public:
    explicit CfooHolder(Cfoo& foo) : foo_(foo) {};
    void SetI(int i) { foo_.SetI(i); }
    int GetI() const { return( foo_.GetI() ); }
private:
    Cfoo& foo_;
};

void bar(CfooHolder &holder, int i)
{
    holder.SetI(i); // fine
}

void bar(CfooHolder const &constholder, int i)
{
    holder.SetI(i);
    // error: method exists, but I can't call it here
}