Const overloading and polymorphysm - c++

I have an accessor member function (e.g. operator[]) that is const-overloaded:
class Container {
public:
Foo& operator[](int i);
const Foo& operator[](int i) const{
return const_cast<Container *>(this)->operator[](i);
}
};
Here, const Foo& operator[] const is defined in this way so that the same thing is not defined twice.
Now I want to make Container a base class, and operator[] becomes virtual:
class BaseContainer {
public:
virtual Foo& operator[](int i) = 0;
const Foo& operator[](int i) const{
// Is this correct?
return const_cast<BaseContainer *>(this)->operator[](i);
}
};
class DerivedContainer : public BaseContainer {
public:
Foo& operator[](int i);
};
Since it is illegal to const_cast from const DerivedContainer * to BaseContainer *, I am not sure if this works in the case of polymorphism.
I would assume that the cast is still valid because the type of this would always be const BaseContainer * in BaseContainer::operator[] const because it is not virtual, but I am not sure if that's the correct way of doing this. Maybe it is simply better to define operator[] twice in this case?

would assume that the const_cast is still valid because the type of this would always be const BaseContainer * in BaseContainer::operator[] const because it is not virtual, but I am not sure if that's the correct way of doing this.
Your understanding is correct. The code should work as intended.
There is another thing you have to think about, though. When you declare
Foo& operator[](int i);
in the derived class, the const version is not going to be found if the function call is made on a derived class object/reference/pointer. To be able to use it with a derived class object/reference/pointer, add the following in the derived class.
using BaseContainer::operator[];

The overloaded non-const-version in DerivedContainer will be called from the body of BaseContainer::const operator[] due to polymorphism. So from this point it is actually a "legal" design, although your assumption "this would always be const BaseContainer * in BaseContainer::operator[] const because it is not virtual" is - in the context of polymorphism - wrong.
See the following code illustrating the call chain:
struct Base {
virtual void print() { cout << "Base.non-const;"; }
void print() const { cout << "entry:Base.const;then..."; const_cast<Base *>(this)->print(); }
};
struct Derived : public Base {
void print() override { cout << "Derived.non-const;"; }
};
int main() {
const Base* bc = new Derived;
bc->print();
//Output: entry:Base.const;then...Derived.non-const;
cout << endl;
Base* bnc = new Derived;
bnc->print();
// Output: Derived.non-const;
}
Note that the non-const-body of operator[] must not alter the *this-object if this object has originally been defined as const. Otherwise you get undefined behaviour.

Related

How Call child method from parent class

I did a small exemple to try to explain you with my poor english what I want to do :).
I have a main class who is my engine. This is my parent class of several children.
this is the parent class :
#include <string>
#include <iostream>
#include <vector>
template <typename Type>
class A
{
public:
A(std::string const &str)
: m_str(str)
{
}
void run(void) const {
unsigned int i;
for(i = 0; ACTIONS[i].f != nullptr; i++) {
if(m_str == ACTIONS[i].key) {
return ((*(this).*ACTIONS[i].f)(m_str));
}
}
}
protected:
typedef struct s_action {
std::string key;
void (Type::*f)(std::string const &);
} t_action;
static t_action const ACTIONS[];
std::string m_str;
};
class B : public A<B>
{
public:
B(std::string const &str);
protected:
static t_action const ACTIONS[];
void error(std::string const &str);
void success(std::string const &str);
};
I would like to call children method with table pointer of member function in this parent class A::run as you can see above
This code does not compile.
I know it's not possible to have a static variable virtual, but it's
exactly that I need to do have for A::ACTIONS. I absolutely need to initialise B::ACTIONS to A::run works.
In first Is it possible? Have you got a small exemple of this case?
This is the end of my small code :
#include "Class.hpp"
B::t_action const B::ACTIONS[] = {
{"ERROR", &B::error},
{"SUCCESS", &B::success},
{"", nullptr}
};
B::B(std::string const &str)
: A<B>(str)
{
}
void B::error(std::string const &str) {
std::cerr << str << std::endl;
}
void B::success(std::string const &str) {
std::cout << str <<std::endl;
}
And the main:
#include "Class.hpp"
int main() {
B b("SUCCESS");
b.run();
return (0);
}
I didn't try, normally this code should Display SUCCESS on stdout
Thank you for your help
void run(void) const
{
unsigned int i;
for(i = 0; ACTIONS[i].f != nullptr; i++)
if (m_str == ACTIONS[i].key)
return ((*(this).*ACTIONS[i].f)(m_str));
}
There are multiple reasons why this fails to compile. Not one, but several reasons. This entire dispatching mechanism must be completely redesigned.
The first order of business is that this is a
void run(void) const
A const class method.
The method pointer in question is:
void (Type::*f)(std::string const &);
The method pointer is not const, but mutable. From an existing const class method, you can only invoke other const methods. You cannot invoke non-const methods, either directly or indirectly via a method pointer, from a const class methods.
So the first order of business is to change this to
void (Type::*f)(std::string const &) const;
This also means that all your methods, in the child class, error() and success(), must also be const class methods too.
If it's necessary to use this dispatch mechanism with non-const methods, the run() method cannot be a const class method itself. But this is not the only problem here, so I'll continue with the const method, at hand.
return ((*(this).*ACTIONS[i].f)(m_str));
The this here, is a A<Type>. This is a method of that class. That's what this is here.
The method pointer, f is pointer to a method of Type, not A<Type>. Type is a subclass of A<Type>, and you cannot convert a pointer or a reference to a base class to a pointer or a reference to a subclass, any more than you can take a pointer to A, and convert to a pointer to B when B inherits from A. C++ does not work this way.
The solution is simple, and requires only a few small tweaks. This run() should take a reference to const Type &, and invoke the method via the passed-in reference, then a replacement abstract run() method invokes it, passing *this as a parameter:
public:
virtual void run()=0;
protected:
void run_me(const Type &me) const
{
unsigned int i;
for(i = 0; ACTIONS[i].f != nullptr; i++)
if (m_str == ACTIONS[i].key)
return ((me.*ACTIONS[i].f)(m_str));
}
Then, each subclass that inherits this template only needs to implement a simple facade:
class B : public A<B>
{
public:
void run() const override
{
run_me(*this);
}
EDIT: This addresses the compilation error, but additional work is needed to deal with the fact that static class members cannot be overridden. The solution is also pretty simple: also leverage virtual class methods in order to implement this.
Remove the declaration of ACTIONS from the template base class, and replace it with an abstract function:
virtual const t_action *get_actions() const=0;
And use it in run_me():
const t_action *ACTIONS=this->get_actions();
The rest of run_me() remains as is, and then implement get_actions() in the child class:
const t_action *get_actions() const override
{
return ACTIONS;
}
Pretty much everything else remains the same.
The problem is that A will always use is own defined set of actions, not B's.
You don't need to create A at all, as you want to use B methods and list of methods.
Let's say that you create first a run call function:
template<typename T>
void run(T* obj, const std::string method)
{
const auto& available_methods = obj->get_methods();
auto iter = available_methods.find(method);
if(iter == available_methods.end())
{
// Handle this case
}
std::invoke(iter->second, obj); //C++17, or (obj->*(iter->second))();
}
Now for the class B, you need something very simple:
class B
{
public:
typedef std::unordered_map<std::string, void(B::*)()> MethodMap;
void foo();
static MethodMap& get_methods()
{
static MethodMap map{{"foo", &B::foo}};
return map;
}
};
Populate the map with get_methods() in the static function, and then call run through:
int main()
{
B b;
run(&b, "foo");
}
If you are going to use CRTP, IMO you need to google for CRTP first.
By the way here's a quick direct ans 2 your q:
template<typename crtp_child>
class crtp_base{
using crtp_target=crtp_child;
auto crtp_this(){
return static_cast<crtp_target*>(this);
};
auto crtp_this() const {
return static_cast<crtp_target const*>(this);
};
public:
void run(){
auto range=crtp_this()->actions.equal_range(m_str);
for(auto entry:range)
(crtp_this()->*(entry.second))(m_str);
};
protected:
crtp_base(std::string str):
m_str(str)
{};
std::string m_str;
//...
};
struct crtp_user:
crtp_base<crtp_user>
{
using crtp_base::crtp_base;//ctor fwding
protected:
friend class crtp_base<crtp_user>;
std::unordered_multimap<std::string, void (crtp_user::*)(std::string)> actions;
//...
};

C++ private polymorphic implementation design

This is a C++(11) question.
I have a object Obj myObj encapsulating an object f of type MyType.
Depending on runtime context, the object fshould behave differently.
One natural way of implementing this would be for the class Obj to encapsulate a pointer to an abstract base class MyType, which would, depending on the context point to different (public) child of MyType, such as MyType1, MyType2, etc.
However, I'm not very keen on Obj "suffering" the consequences of MyType being polymorphic, i.e. having to deal with a pointer. In particular, if I make it a std::unique_ptr<MyType>, it implies that Obj can either not be copied or that one needs to give it a proper copy constructor that deals with copying MyType ressources.
In my opinion, MyType being polymorphic shouldn't be Obj's problem.
I came with the following classes. Essentially the idea is to hide the pointer within MyTypeprivate attributes. In addition my second question concerns the fact that concrete implementations of MyTypeImpl may share some code shouldn't be repeated. I've put that in a class from which concrete implementations privately inherit.
I'm curious what more expert developers than me would think about it. Is it too heavy "just to hide the pointer"? Is there a better way to do it?
#include <iostream>
#include <memory>
// a "standard" implementation of MyType
class MyTypeImpl
{
public:
virtual double operator()(double a) = 0;
virtual int implType() const = 0;
virtual void complexStuff() const = 0;
};
// some internal stuff common to all implementations
class MyTypeImplInternals
{
protected:
MyTypeImplInternals(int value):factor_{value}{}
int factor_;
void longCommonFunction() const{ std::cout << "I'm doing complex stuff common to all interfaces " << factor_ << "\n" ;}
};
// one specific implementation
class MyTypeImpl1: public MyTypeImpl, private MyTypeImplInternals
{
public:
MyTypeImpl1(int factor):MyTypeImplInternals{factor}{};
virtual double operator()(double a) override {return factor_*a;}
virtual int implType() const override {return 1;}
virtual void complexStuff() const override { longCommonFunction(); }
};
// a second implementation
class MyTypeImpl2: public MyTypeImpl, private MyTypeImplInternals
{
public:
MyTypeImpl2(int factor):MyTypeImplInternals{factor}{};
virtual double operator()(double a) override {return factor_*a;}
virtual int implType() const override {return 2;}
virtual void complexStuff() const override { longCommonFunction(); }
};
class MyTypeImplFactory
{
public:
static std::unique_ptr<MyTypeImpl>createMyTypeImpl(int implementationType)
{
switch(implementationType)
{
case 1:
return std::unique_ptr<MyTypeImpl> (new MyTypeImpl1(12));
case 2:
return std::unique_ptr<MyTypeImpl> (new MyTypeImpl2(22));
default:
throw std::runtime_error("implementation does not exist...\n");
return nullptr;
}
}
};
// my type
class MyType
{
public:
MyType(int implementationType)
{
implPtr_ = MyTypeImplFactory::createMyTypeImpl(implementationType);
}
MyType(MyType const& source)
: implPtr_{ MyTypeImplFactory::createMyTypeImpl(source.implType()) }
{
}
double operator()(double a){return (*implPtr_)(a);}
int implType() const {return implPtr_->implType();}
void complexStuff() const {implPtr_->complexStuff();}
private:
std::unique_ptr<MyTypeImpl> implPtr_;
};
class Obj
{
private:
MyType f;
public:
Obj(int dim):f{dim}{}
Obj(Obj&& sourceToMove) = default;
Obj(Obj const& source) = default;
void doStuff() {std::cout << "I'm doing stuff() " << f(2) << std::endl; f.complexStuff();}
};
int main()
{
Obj myObj{1}, myObj2{2};
myObj.doStuff();
myObj2.doStuff();
Obj myObj3{std::move(myObj2)}; // myObj2 now dead
Obj myObj4{myObj};
myObj3.doStuff();
myObj4.doStuff();
}
link to online compiler : http://cpp.sh/8rhyy
Here the implementations are very dumb ones to serve as an example. An application for this design could be for instance a Solver (Obj) that solves some kind of physics Equation (MyType) which exact definition depends on the dimensionality of the problem, equation in 1D space is not the same as in 2D or in 3D. Solver's code would be completely independent on Equation's dimensionality and also wouldn't have to deal with a pointer. Equation would hide its 1D, 2D, or 3D implementation from outside's world.
(was originally a post on code review that was put on Hold because to abstract)
This proposed class design appears to have an obvious problem. The polymorphic type is referenced by a std::unique_ptr:
std::unique_ptr<MyTypeImpl> implPtr_;
Obj's default copy constructor, and assignment operator will end up transferring the held pointer to the new object, leaving the std::unique_ptr in the original object staring at a nullptr. Not good.
At the bare minimum this should be either a std::shared_ptr, or Obj's copy constructor and assignment operator will need to instantiate a new implPtr_. Note that with the easy std::shared_ptr fix the result of the copy constructor and an assignment operator is having multiple instances of Obj referencing the same instance of MyTypeImpl, which may or may not be an issue.
A much simpler class design is simply have MyTypeImpl1 and MyTypeImpl2 be subclasses of Obj, implementing the required polymorphic behavior.
I just refactored your codes.
#include <iostream>
#include <memory>
// !abstraction
class MyType
{
public:
virtual double operator()(double a) = 0;
virtual int implType() const = 0;
virtual void complexStuff() const = 0;
};
// !!MyTypeImplInternals could be a super class of MyTypeImpl* if it has properties(such as factor_) or just some static functions.
class MyTypeImplInternals
{
public:
MyTypeImplInternals(int value):factor_{value}{}
int factor_;
void longCommonFunction() const{ std::cout << "I'm doing complex stuff common to all interfaces " << factor_ << "\n" ;}
};
// one specific implementation
class MyTypeImpl1: public MyType
{
MyTypeImplInternals internal_;
public:
MyTypeImpl1(int factor):internal_{factor}{};
virtual double operator()(double a) override {return internal_.factor_*a;}
virtual int implType() const override {return 1;}
virtual void complexStuff() const override { internal_.longCommonFunction(); }
};
// a second implementation
class MyTypeImpl2: public MyType
{
MyTypeImplInternals internal_;
public:
MyTypeImpl2(int factor):internal_{factor}{};
virtual double operator()(double a) override {return internal_.factor_*a;}
virtual int implType() const override {return 2;}
virtual void complexStuff() const override { internal_.longCommonFunction(); }
};
std::unique_ptr<MyType> createMyType(int implementationType)
{
switch(implementationType)
{
case 1:
return std::unique_ptr<MyType> (new MyTypeImpl1(12));
case 2:
return std::unique_ptr<MyType> (new MyTypeImpl2(22));
default:
throw std::runtime_error("implementation does not exist...\n");
return nullptr;
}
}
class Obj
{
private:
std::unique_ptr<MyType> f_;
public:
Obj(int dim):f_(createMyType(dim)){}
Obj(Obj&& sourceToMove) : f_(std::move(sourceToMove.f_)) {}
Obj(Obj const& source) : f_(createMyType(source.f_->implType())) {}
void doStuff() {std::cout << "I'm doing stuff() " << (*f_)(2) << std::endl; f_->complexStuff();}
};
int main()
{
Obj myObj{1}, myObj2{2};
myObj.doStuff();
myObj2.doStuff();
Obj myObj3{std::move(myObj2)}; // myObj2 now dead
Obj myObj4{myObj}; //!!Bad idea to share an implementation to more Objs.
myObj3.doStuff();
myObj4.doStuff();
}

How to overload operator << for derived classes using a shared base class?

I'm trying to overload operator<< in several subclasses.
I have a superclass called Question, which has an enumerated value type, and a string question.
The subclasses of this class are TextQuestion, ChoiceQuestion, BoolQuestion and ScaleQuestion. TextQuestion has no additional data fields. ChoiceQuestion has a vector of strings, to store the multiple choice possibilities. BoolQuestion has no additional data fields. ScaleQuestion has two int-values, low_ and high_, for the scale.
class Question {
public:
enum Type{TEXT, CHOICE, BOOL, SCALE};
Question():
type_(), question_() {}
Question(Type type, std::string question):
type_(type), question_(question) {}
friend std::ostream& operator<<(std::ostream& out, const Question& q);
virtual void print(std::ostream& out) const;
virtual ~Question();
private:
Type type_;
std::string question_;
};
class TextQuestion: public Question {
public:
TextQuestion():
Question() {}
TextQuestion(Type type, std::string question):
Question(type, question) {}
void print(std::ostream& out) const;
virtual ~TextQuestion();
};
class ChoiceQuestion: public Question {
public:
ChoiceQuestion():
Question(), choices_() {}
ChoiceQuestion(Type type, std::string question, std::vector<std::string> choices):
Question(type, question), choices_(choices) {}
void print(std::ostream& out) const;
virtual ~ChoiceQuestion();
private:
std::vector<std::string> choices_;
};
class BoolQuestion: public Question {
public:
BoolQuestion():
Question() {}
BoolQuestion(Type type, std::string question):
Question(type, question) {}
void print(std::ostream& out) const;
virtual ~BoolQuestion();
};
class ScaleQuestion: public Question {
public:
ScaleQuestion():
Question(), low_(), high_() {}
ScaleQuestion(Type type, std::string question, int low = 0, int high = 0):
Question(type, question), low_(low), high_(high) {}
void print(std::ostream& out) const;
virtual ~ScaleQuestion();
private:
int low_, high_;
};
Now, I'm trying to overload operator<< for all these subclasses and I tried using this example
So I made a virtual print function in the superclass, overloaded the print function in each subclass and the operator<< in the superclass calls the print-function.
std::ostream& operator<<(std::ostream& out, const Question& q) {
q.print(out);
return out;
}
void Question::print(std::ostream& out) const {
std::string type;
switch(type_) {
case Question::TEXT:
type = "TEXT";
break;
case Question::CHOICE:
type = "CHOICE";
break;
case Question::BOOL:
type = "BOOL";
break;
case Question::SCALE:
type = "SCALE";
break;
}
out << type << " " << question_;
}
void TextQuestion::print(std::ostream& out) const {
Question::print(out);
}
void ChoiceQuestion::print(std::ostream& out) const {
Question::print(out);
out << std::endl;
int size(get_choices_size());
for (int i = 0; i < size; ++i) {
out << choices_[i] << std::endl;
}
}
void BoolQuestion::print(std::ostream& out) const {
Question::print(out);
}
void ScaleQuestion::print(std::ostream& out) const {
Question::print(out);
out << " " << low_ << " " << high_;
}
I did it exactly like in the example, but when I output my Questions, it always uses the baseclass and outputs only the type and the question. The compiler never uses the subclasses.
Only virtual functions allow for dispatch according to the dynamic type of an object (given the static type of a base class).
Only nonstatic member functions can be virtual.
operator << cannot be virtual because it cannot be a nonstatic member function, because its first argument must be the stream.
You can call a virtual function from an operator << which accepts a reference to the base class.
Calling a virtual function from a non-virtual function, to preserve interface aspects which are not overridden, is called the non-virtual idiom.
Ah, I did not see any virtual upon first read but now I do. If you are not getting subclass behavior where expected, a likely culprit is slicing, where you create an object of base class type and assign it a value from a subclass object.
TextQuestion q( "What is hello, world?" ); // Original object
Question & qr( q ); // Reference, not another object
Question q2( q ); // Base class object with copied subset (slice) of data.
std::cout << q << '\n'; // Observe subclass behavior.
std::cout << qr << '\n'; // Observe subclass behavior due to dynamic typing.
std::cout << q2 << '\n'; // Observe superclass behavior due to slicing.
You cannot override operator<< in your classes because it cannot be a member (you cannot pass this as the right operand).
Instead, have a virtual print function overriden in each subclass, and define global/nanespace scope operator<< in terms of it.

Mixing interfaces and nested classes in C++

I am pretty new to C++. Today, I am experiencing some issues in mixing nested classes and interfaces.
I wrote a small (useless) program that will be more effective at explaining my issue than long sentences:
#include <iostream>
#include <vector>
class SomeInterface {
public:
virtual int SomeMethod() = 0;
class reference {
public:
virtual operator int() const = 0;
virtual reference& operator=(int x) = 0;
};
virtual reference operator[](unsigned int pos) = 0;
};
class A : public SomeInterface {
public:
A(unsigned int size) { vec_.resize(size, 0); }
int SomeMethod() { return 1; }
class reference : public SomeInterface::reference {
public:
reference(std::vector<int>::reference ref) : ref_(ref) { }
operator int() const { return (int) this->ref_; }
reference& operator=(int x) { this->ref_ = x; return *this; }
private:
std::vector<int>::reference ref_;
};
reference operator[](unsigned int pos) {
return reference(this->vec_[pos]);
};
private:
std::vector<int> vec_;
};
int main() {
A a(10);
a[5] = 42;
std::cerr << a[5] << std::endl;
return 0;
}
Here, the program compiles fine if I remove the line virtual reference operator[](unsigned int pos) = 0; in the interface. However, I would like the array subscript operator to be part of the interface.
The error message thrown by G++ is invalid abstract return type for member function ‘virtual SomeInterface::reference SomeInterface::operator[](unsigned int)’.
I do understand why it fails. But I can't figure out any way to make something like this work. Can anybody explain why I am doing (or thinking) wrong?
You can not create objects of type SomeInterface::reference, since it is a pure abstract class, and that is what the compiler told you.
You need to return a reference (or a pointer) to such class. Like this :
virtual reference& operator[](unsigned int pos) = 0;
but then :
in derived classes, you shouldn't change the signature of the pure virtual methods. It should stay virtual SomeInterface::reference& operator[](unsigned int pos)
you can not return reference to a temporary object
btw take care how you create objects of such classes. They do not have virtual destructors.
Basically you can't return a reference to something that can never exist. However, you could use a pointer to an abstract class. That pointer will ultimately only be able to point to an instance of a derived class.
It is not clear exactly what you are trying to do. But you might look into Creation Patterns to find something close to what you need

Single class which combines const and nonconst reference data member

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?
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.
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); }
};
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.
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.
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
}