Putting a constant member in a base

D

Dennis Jones

Hello,

I have a hierarchy of classes in which there will be a data element that is
common to all descendant classes. This element (a string in this case) is
constant, but specific to each class. So I have something like this:

class Base
{
private:
virtual std::string GetString() const = 0;
public:
void SomeFunction() { GetString(); }
};

class Derived : public Base
{
private:
const std::string FString;
virtual std::string GetString() const { return FString; }
public:
Derived()
: Base(),
FString( "Derived" ) {}
};

This isn't completely unreasonable, but I do have to duplicate the 'FString'
data element and its getter function in every derived class. It would be
much nicer if I could do something like this instead:

class Base
{
private:
const std::string FString;
std::string GetString() const { return FString; }
public:
void SomeFunction() { GetString(); }
};

class Derived : public Base
{
public:
Derived()
: Base(),
FString( "Derived" ) {}
};

However, this won't work because the compiler will complain that "'FString'
is not an unabiguous base class of 'Derived'" which, of course, is true and
makes perfect sense. A less desirable (but still reasonable) solution would
be to initialize 'FString' in the constructor's body:

class Derived : public Base
{
public:
Derived()
: Base()
{
FString = "Derived";
}
};

But of course, this won't work either because 'FString' is const! What's
one to do? Is there a way to eliminate the unnecessary duplication and
maintain the constness of the data element at the same time?

Thanks,

- Dennis
 
D

Dennis Jones

Dennis Jones said:
Hello,

I have a hierarchy of classes in which there will be a data element that
is common to all descendant classes. This element (a string in this case)
is constant, but specific to each class. So I have something like this:

class Base
{
private:
virtual std::string GetString() const = 0;
public:
void SomeFunction() { GetString(); }
};

class Derived : public Base
{
private:
const std::string FString;
virtual std::string GetString() const { return FString; }
public:
Derived()
: Base(),
FString( "Derived" ) {}
};

This isn't completely unreasonable, but I do have to duplicate the
'FString' data element and its getter function in every derived class. It
would be much nicer if I could do something like this instead:

class Base
{
private:
const std::string FString;
std::string GetString() const { return FString; }
public:
void SomeFunction() { GetString(); }
};

class Derived : public Base
{
public:
Derived()
: Base(),
FString( "Derived" ) {}
};

However, this won't work because the compiler will complain that
"'FString' is not an unabiguous base class of 'Derived'" which, of course,
is true and makes perfect sense. A less desirable (but still reasonable)
solution would be to initialize 'FString' in the constructor's body:

class Derived : public Base
{
public:
Derived()
: Base()
{
FString = "Derived";
}
};

But of course, this won't work either because 'FString' is const! What's
one to do? Is there a way to eliminate the unnecessary duplication and
maintain the constness of the data element at the same time?


P.S. I should have mentioned that due to my employer's preferences, using a
constructor argument (as shown below) is not desirable, which is why I am
searching for other alternatives:

class Base
{
private:
const std::string FString;
std::string GetString() const { return FString; }
public:
Base( const std::string &AString )
: FString( AValue ) {}
void SomeFunction() { GetString(); }
};


Thanks,
- Dennis
 
P

Piyo

Dennis said:
Hello,

I have a hierarchy of classes in which there will be a data element that is
common to all descendant classes. This element (a string in this case) is
constant, but specific to each class. So I have something like this:

class Base
{
private:
virtual std::string GetString() const = 0;
public:
void SomeFunction() { GetString(); }
};

class Derived : public Base
{
private:
const std::string FString;
virtual std::string GetString() const { return FString; }
public:
Derived()
: Base(),
FString( "Derived" ) {}
};

This isn't completely unreasonable, but I do have to duplicate the 'FString'
data element and its getter function in every derived class. It would be
much nicer if I could do something like this instead:

class Base
{
private:
const std::string FString;
std::string GetString() const { return FString; }
public:
void SomeFunction() { GetString(); }
};

class Derived : public Base
{
public:
Derived()
: Base(),
FString( "Derived" ) {}
};

However, this won't work because the compiler will complain that "'FString'
is not an unabiguous base class of 'Derived'" which, of course, is true and
makes perfect sense. A less desirable (but still reasonable) solution would
be to initialize 'FString' in the constructor's body:

class Derived : public Base
{
public:
Derived()
: Base()
{
FString = "Derived";
}
};

But of course, this won't work either because 'FString' is const! What's
one to do? Is there a way to eliminate the unnecessary duplication and
maintain the constness of the data element at the same time?

Thanks,

- Dennis

I am not sure I understand what you are looking for but
by any chance you are looking for this?

class Base
{
private:
const std::string FString;
std::string GetString() const { return FString; }
public:
Base( const std::string &val ) : FString( val ) {}
void SomeFunction() { GetString(); }
};

class Derived : public Base
{
public:
Derived()
: Base( "Derived" ) {}
// for ease of use to change the value to something else
Derived( const std::string &val ) : Base(val) {}
};

Hope that helps!
 
I

Ian Collins

Dennis said:
P.S. I should have mentioned that due to my employer's preferences, using a
constructor argument (as shown below) is not desirable, which is why I am
searching for other alternatives:
Well that's just plain daft. Ask them to give a good reason why this is so.
 
P

Piyo

BTW, not only is it not possible because it is const but FString is
private and the derived class cannot access private members of the base.

Having said that:

Try this?

class Base
{
private:
// remove const, your derived classes cannot touch it
std::string FString;
std::string GetString() const { return FString; }
protected:
// allow derived class to set it
void setFString( const std::string &val ) { FString = val; }
public:
void SomeFunction() { GetString(); }
};

class Derived : public Base
{
public:
Derived()
: Base() { setFString( "Derived" ); }
// for ease of use to change the value to something else
Derived( const std::string &val ) : Base() { setFString(val);}
};

Does that work for you?
 
P

Piyo

Ian said:
Well that's just plain daft. Ask them to give a good reason why this is so.
Ok all joking aside, Here is a good reason why you do not want to have a
constructor argument: DefaultConstructable concept that is required by
say std::vector<>.

The problem though I see is the conflicting requirements.
DefaultConstructible vs const initialization. A const member variable
cannot be initialized except via the constructor's member
initialization. Otherwise, the const member function will simply always
be its default value.
 
P

paul.joseph.davis

Ok all joking aside, Here is a good reason why you do not want to have a
constructor argument: DefaultConstructable concept that is required by
say std::vector<>.

True indeed. But if the hypothetical base class has pure virtual
functions it'd also be impossible to use it in an stl container (as
anything other than a pointer).
The problem though I see is the conflicting requirements.
DefaultConstructible vs const initialization. A const member variable
cannot be initialized except via the constructor's member
initialization. Otherwise, the const member function will simply always
be its default value.

I don't have a copy of the standard but it makes sense that the only
class able to initialize a member variable is the one in which it is
defined. (IE, a class can't initialze an inherited member variable).

And like the parent poster noted, this is a const initialization vs.
default constructable question.

So I'd probably work on convincing your boss that discarding the
option of constructor arguments out of hand is silly. Especially if
that base class has pure virtual methods.

HTH,
Paul Davis
 
D

Dennis Jones

class Base
{
private:
// remove const, your derived classes cannot touch it
std::string FString;
std::string GetString() const { return FString; }
protected:
// allow derived class to set it
void setFString( const std::string &val ) { FString = val; }
public:
void SomeFunction() { GetString(); }
};

class Derived : public Base
{
public:
Derived()
: Base() { setFString( "Derived" ); }
// for ease of use to change the value to something else
Derived( const std::string &val ) : Base() { setFString(val);}
};

Does that work for you?

Yes, a setter with a private member just might be what the doctor ordered.

- Dennis
 
J

John Carson

Dennis Jones said:
P.S. I should have mentioned that due to my employer's preferences,
using a constructor argument (as shown below) is not desirable, which
is why I am searching for other alternatives:

class Base
{
private:
const std::string FString;
std::string GetString() const { return FString; }
public:
Base( const std::string &AString )
: FString( AValue ) {}
void SomeFunction() { GetString(); }
};


Perhaps there is some reason for this "preference" but, on the face of
things, your employer is an idiot.
 
?

=?ISO-8859-15?Q?Juli=E1n?= Albo

Dennis said:
I have a hierarchy of classes in which there will be a data element that
is common to all descendant classes. This element (a string in this case)
is constant, but specific to each class. So I have something like this:

class Base
{
private:
virtual std::string GetString() const = 0;
public:
void SomeFunction() { GetString(); }
};

class Derived : public Base
{
private:
const std::string FString;
virtual std::string GetString() const { return FString; }
public:
Derived()
: Base(),
FString( "Derived" ) {}
};

This isn't completely unreasonable, but I do have to duplicate the
'FString' data element and its getter function in every derived class.

You can simply drop the data element:

class Base
{
private:
virtual std::string GetString() const = 0;
public:
void SomeFunction() { GetString(); }
};

class Derived : public Base
{
private:
virtual std::string GetString() const { return "Derived"; }
};
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

Forum statistics

Threads
473,777
Messages
2,569,604
Members
45,233
Latest member
AlyssaCrai

Latest Threads

Top