0

Here's a set of C++ classes which implement a kind of adapter pattern:

#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);
}

I want to combine CfooHolderNonConst and CFooHolderConst into a single class, or failing that, inherit one from the other. The reference to Cfoo is a problem here, because in CFooHolderConst it needs to be defined as const Cfoo&, while in CfooHolderNonConst it needs to be Cfoo&.

This is a similar problem to the interator/const_iterator here: How to avoid code duplication implementing const and non-const iterators?

...but I'm hoping that because this doesn't have to meet the STL iterator requirements, there might be a simpler solution.

In the past I've solved this kind of problem by having both a const and nonconst pointer as class members, and setting one or the other up from overloaded constructors. This wastes space and seems clumsy. Is there a more elegant solution?

5 Answers 5

1

Yes, it can be done:

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;    

Actually this is intended to be an example where you wrap the getting of the underlying data in its const or non-const state. The Reader holds a non-const reference unless the templated type is const, but doesn't let you write to it, so you can extend it as with CHolderReaderWriter when you do need to write to it.

5
  • My suggestion would be to not make the data member protected, as it breaks encapsulation, but rather to use friendship.
    – AndrzejJ
    Commented Feb 21, 2012 at 15:07
  • I prefer not to give friendships from base classes to derived classes. Normally I would use a protected get() to get to the underlying. You can use private inheritence and using to bring the Get method to public from the base class if you really insist. I am just illustrating here how it can be done in this case.
    – CashCow
    Commented Feb 21, 2012 at 18:11
  • This looks promising. I'll probably go with either your or AndrzejJ's solution. Commented Feb 21, 2012 at 22:07
  • For me this is not a typical situation to use inheritance in the first place, just a trick to get the compiler do what I need. Private inheritance would work as well, but assuming the interface may be more complicated and subject to changes it becomes more problematic, as you need to declare all const functions to be public, and then maintain it. And on top of that you'd have to make the const version of the class also privately inherit the template and again expose all the inherited functions of the public interface. Any particular reason you'd not use friendship in this case?
    – AndrzejJ
    Commented Feb 21, 2012 at 22:08
  • There are ways to "seal" a class so you can't inherit from it. (private virtual inheritence). You could do that here if you religiously object to a protected member variable. But really you are arguing on style. The protected member variable looks simpler to me and the fact I don't have a virtual destructor or any virtual functions suggests other classes should not be deriving from mine.
    – CashCow
    Commented Feb 22, 2012 at 14:46
1

You can create a template class that will give you const functionality for both const and non-const versions of the class, and then inheritance to extend the non-const version with functionality to modify the member:

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); }
};
3
  • We apparently posted in a short interval of one another - didn't see your answer when I posted mine.
    – AndrzejJ
    Commented Feb 21, 2012 at 14:56
  • We were answering at the same time with the same solution. I have come across this kind of situation before in real life code.
    – CashCow
    Commented Feb 21, 2012 at 18:13
  • I'll do some more work on this and probably go with either your or CashCow's solution. Unfortunately I can't give you both the accepted answer, so I'll pick whoever my final code looks closest to. Commented Feb 21, 2012 at 22:06
0

I think it is good idea to have both const- and non-const- interface as a separate classes.

They provide different interfaces to their users and have different semantics. The duplication is also minimal in your example.

0

If you really want to have the same class (providing the semantics still make sense), then I think you want something like:

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

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

I think that your hope would be for C++ to make the following decissions: a) Treat the Cfoo object as const if it was const, or non-const if it was non-const. The clue is both the definition of Cfoo and the definition of CfooHolder. If Cfoo is const, then CfooHolder must be declared const, or it should fail to compile. If Cfoo is non-const, then you can create CfooHolder's which can be both const and non-const. b) The method SetI() should cease to compile when used in a const CfooHolder object. In the example above h1.SetI( 6 ); should not compile.

My answer is that, if a) worked, then b) would automatically work as well. The problem is achieving a), which is not possible as far as I know.

For this to work, the attribute should be made const or non-const under the circunstance of an object of its class being const or non-const. Though an object of the class can change this "state", the attributes remain the same, however. But you can only use const methods when the object of this class is const (for example, when a parameter is passed by constant reference). So, C++ won't support that, because it does not work that way.

The other possibility would be to let the attribute itself be const and non-const at the same time, which does not make sense.

The short answer is: it can't be done and there will be code repetition. If you really want to avoid this, and the wrapper is enough complex to be worried, the only way is to create a general holder, and then the constant and non-constant wrappers around the general holder, avoiding repetition to the bare minimal.

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!" ); }
};

It is not perfect, but it works under the stated terms. The method SetI() would throw a runtime error, but if the CfooHolderConst object is declared as const, then a call to SetI() won't even compile.

Hope this helps.

0

Is there a specific reason why you can't just use FooHolder for non-const (mutable) access, and const FooHolder for const access?

You can't call a non-const-qualified method (like SetI) on a const object, so it seems like it does what you want. Obviously you need to create the holder object from a non-const Cfoo originally, though.

example:

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
}
2
  • "Obviously you need to create the holder object from a non-const Cfoo originally, though." That's a problem. I really need to be able to instantiate the class based on a const Cfoo. I don't much want to have a const_cast in there, even though it would be reasonably safe. Commented Feb 23, 2012 at 10:09
  • It's going to be difficult to have the (compile-time) type system to enforce something you don't know until runtime. You could have an abstract base class and instantiate one of two derived versions in a factory function, but I think that falls foul of your wastes space and seems clumsy test. Likewise for just keeping bool amIConst in the holder and checking it everywhere.
    – Useless
    Commented Feb 23, 2012 at 10:15

Not the answer you're looking for? Browse other questions tagged or ask your own question.