C++ re-assignable const member variable - c++

In C++, suppose I have a member variable of type Bar, and I want to make sure only const methods are called on it. So then AFAIK I can declare it as a const Bar member variable like below:
class Foo
{
public:
Foo(const Bar& b) : bar(b) {}
void setBar(Bar& b) {
bar = b; // compiler complains here
}
private:
const Bar bar;
}
But I also want to be able to re-assign bar altogether. Is this possible to do without the compiler complaining?

The "trick" is to have your member data be a std::shared_ptr<const Bar> instead of const Bar; you can then re-assign the shared_ptr at will, but can't change Bar since the pointer is const.
#include <memory>
struct Bar final {};
struct Foo final
{
//Foo(const Bar& b) : bar(b) {}
Foo(const Bar& b) : pBar(std::make_shared<const Bar>(b)) {}
void setBar(const Bar& b) {
//bar = b; // compiler complains here
pBar = std::make_shared<const Bar>(b);
}
private:
//const Bar bar;
std::shared_ptr<const Bar> pBar;
};
Note that std::unique_ptr might be better than std::shared_ptr, but then you've got to add copy/assignment, assuming you want those. However, since it's a shared_ptr to constBar, it's not all that bad.

const members create more problems than they solve. If you want to make sure to only call const methods on bar but you want it not to be const (because you want to reassign it) then you can add another layer of encapsulation. For example:
struct Bar {
void doSomething() const {}
};
struct BarHolder {
BarHolder(const Bar& b) : bar(b) {}
const Bar& get() {return bar;}
void set(const Bar& b) { bar = b;}
private:
Bar bar;
};
class Foo {
public:
Foo(const Bar& b) : bar(b) {}
void doSomething() {
bar.get().doSomething(); // <- can only call const methods
}
void setBar(Bar& b) {
bar.set(b);
}
private:
BarHolder bar;
};
It didn't occur to me at first, but as mentioned in a comment, you similarly get "another layer of encapsulation" by using a std::unique_ptr<const Bar>, though then the object will be actually const.

If you want the Bar member variable to be re-assignable, then you don't want it to be const. This doesn't mean you can't call the const member functions on it; it just means that if a competing non-const overload exists, it will call the non-const one -- for example:
struct Bar {
// (1)
auto get_baz() -> Baz&;
// (2)
auto get_baz() const -> const Baz&;
};
Bar bar;
const Bar cbar;
auto& b1 = bar.get_baz(); // calls (1) because bar is not const
auto& b2 = cbar.get_baz(); // calls (2) because cbar is const
This issue does not happen if there are no non-const competing overloads:
struct Bar {
auto get_buzz() const -> const Buzz&;
};
Bar bar;
const Bar cbar;
// These both call the one get_buzz
auto& b1 = bar.get_buzz();
auto& b2 = cbar.get_buzz();
If you want to explicitly force the call to the const overload, you just need to make sure you call it from a const-qualified object or reference instead. This can be done easily with c++17's std::as_const
const Baz& b = std::as_const(bar).get_baz(); // calls (2)
If you don't have access to C++17, then you could also just form a const reference to Bar either by using a static_cast, or by using a temporary const reference, such as:
const Baz& b = static_cast<const Bar&>(bar).get_baz();
// or
const auto& const_bar = bar;
const Baz& b = const_bar.get_baz();

The first thing I'd point out is that your understanding of "const" methods is a bit off: a const method does not mean that it can modify const members, but that it can be called on a const instance of that class.
Eg if you have
class Foo {
public:
void method1() const { ... };
void method2() { ... };
}
and then a const instance of Foo as
const Foo foo1;
then you could call
foo1.method1()
becuase method1 is const, as is foo1; but you could not call method2, because foo1 is const, and method2 is not.
That said, what you are looking for is probably the "mutable" keyword on your member: usually a const method will not allow changing any members of that class (well, because the instance of that calss is const!); however, you can tag individual members of the class to be "mutable", which will allow const methods to change that member.
Eg, if you have
class Foo { public:
int a;
mutable int b;
void method() const { ... }
}
Then Foo::method() would be allowed to change 'a', but not 'b'.
Once you declare a member of a class as const, then only the constructor can set this member.

Related

Is const reference to a global constant good practice?

Trying to figure out how to efficiently and safely reference global custom type constants that are initialized at compile time. I want to avoid copying of Foo as I will be creating a lot of Bar instances and Foo is a bit more complicated than shown here. Keeping a const reference from Bar to Foo appears to be safe (or is it?) but at the same time I don't know how to prevent the "badBar" usage if possible. Any suggestions?
class Foo
{
int a_, b_;
public:
constexpr Foo(int a, int b) : a_(a), b_(b) {}
};
inline constexpr Foo kFoo{1, 2};
class Bar
{
const Foo& foo_;
public:
Bar(const Foo& foo) : foo_(foo) {}
const Foo& foo() { return foo_; }
};
int main() {
Bar goodBar(kFoo);
Bar badBar(Foo(3, 4)); // ouch
return 0;
}
Yes, keeping a reference to globally defined constant variable is safe as it will always be valid.
You can just initialize the member directly:
class Foo
{
int a_, b_;
public:
constexpr Foo(int a, int b) : a_(a), b_(b) {}
};
inline constexpr Foo kFoo{1, 2};
class Bar
{
const Foo& foo_=kFoo;
};
The rest of the world does not have to know it exists and makes the ctor of Bar simpler.
Although it feels slightly redundant, anywhere you can write foo_, you can also write kFoo. Maybe I am misunderstanding your question?
You can add an rvalue constructor to discard rvalue arguments.
class Bar
{
const Foo& foo_;
public:
Bar(const Foo& foo) : foo_(foo) {}
Bar(Foo &&) = delete; // <-- rvalue constructor
const Foo& foo() { return foo_; }
};

Idiomatic way to prevent slicing?

Sometimes it can be an annoyance that c++ defaults to allow slicing. For example
struct foo { int a; };
struct bar : foo { int b; };
int main() {
bar x{1,2};
foo y = x; // <- I dont want this to compile!
}
This compiles and runs as expected! Though, what if I dont want to enable slicing?
What is the idiomatic way to write foo such that one cannot slice instances of any derived class?
I'm not sure if there is a named idiom for it but you can add a deleted function to the overload set that is a better match then the base classes slicing operations. If you change foo to
struct foo
{
int a;
foo() = default; // you have to add this because of the template constructor
template<typename T>
foo(const T&) = delete; // error trying to copy anything but a foo
template<typename T>
foo& operator=(const T&) = delete; // error assigning anything else but a foo
};
then you can only ever copy construct or copy assign a foo to foo. Any other type will pick the function template and you'll get an error about using a deleted function. This does mean that your class, and the classes that use it can no longer be an aggregate though. Since the members that are added are templates, they are not considered copy constructors or copy assignment operators so you'll get the default copy and move constructors and assignment operators.
Since 2011, the idiomatic way has been to use auto:
#include <iostream>
struct foo { int a; };
struct bar : foo { int b; };
int main() {
bar x{1,2};
auto y = x; // <- y is a bar
}
If you wish to actively prevent slicing, there are a number of ways:
Usually the most preferable way, unless you specifically need inheritance (you often don't) is to use encapsulation:
#include <iostream>
struct foo { int a; };
struct bar
{
bar(int a, int b)
: foo_(a)
, b(b)
{}
int b;
int get_a() const { return foo_.a; }
private:
foo foo_;
};
int main() {
bar x{1,2};
// foo y = x; // <- does not compile
}
Another more specialised way might be to alter the permissions around copy operators:
#include <iostream>
struct foo {
int a;
protected:
foo(foo const&) = default;
foo(foo&&) = default;
foo& operator=(foo const&) = default;
foo& operator=(foo&&) = default;
};
struct bar : foo
{
bar(int a, int b)
: foo{a}, b{b}
{}
int b;
};
int main() {
auto x = bar (1,2);
// foo y = x; // <- does not compile
}
You can prevent the base from being copied outside of member functions of derived classes and the base itself by declaring the copy constructor protected:
struct foo {
// ...
protected:
foo(foo&) = default;
};

Provide default value for constructor initializing reference instance variable

I have a class Foo that has a reference to Bar as an instance variable. Is there a way to have one of the Foo constructors create a Bar instance (so users of the Foo class need not explicitly create a Bar object themselves)?
I'd like to do something like this:
class Foo {
private:
int size;
Bar& bar; // shared by other objects
//several other instance variables
public:
Foo(int s, Bar& b) : size(s), bar(b) {}
// This, of course, doesn't work. I can't think of an
// alternative that doesn't require users of Foo to
// create Bar
Foo(int s) : size(s), bar(Bar(s)) {}
}
(In "real life", Foo and Bar are more complicated. Bar is a friend of Foo that has a private constructor used only by Foo. Each Bar can be shared by several Foos. This isn't a major problem, I'm just curious if it can be done without pointers.)
The simplest solution would be to allocate a Bar if and when you need one. Adding a member std::unique_ptr<Bar> is the easiest way of achieving this. I can't conceive of a system that addresses your problem that will not add some memory overhead to your class. You either have to account for storage for a Bar even when it isn't needed or you have to account for some state to track rather or not your Bar is internally managed. Using a unique_ptr adds the size of a pointer to your class.
#include <memory>
class Foo {
private:
std::unique_ptr<Bar> storage;
Bar& bar;
int size;
public:
Foo(int s, Bar& b) : bar(b), size(s) {}
Foo(int s) : storage(std::make_unique<Bar>(s)), bar(*storage), size(s) {}
};
A more elaborate solution could be to provide a wrapper for Bar that provides a default constructor that takes care of initializing a new instance.
Note that in your example, you do not initialize the member in the same order as they are declared. Members will be initialized in the order they are declared regardless of how you order them in your initialization list.
If every Foo shall have it's own individual Bar-object (not a single shared one), then you could try the following:
class Foo {
private:
Bar& bar; // shared by other objects
int size;
Bar* defaultBar;
//several other instance variables
public:
Foo(int s, Bar& b) : size(s), bar(b), defaultBar(nullptr) {}
Foo(int s) : Foo(s, *(new Bar(14))) { defaultBar=&bar; };
virtual ~Foo() {
if (defaultBar)
delete defaultBar;
}
void setBar(Bar &b) {
if (defaultBar)
delete defaultBar;
defaultBar = nullptr;
bar=b;
}
};
Hope it is what you are looking for.
// This, of course, doesn't work. I can't think of an
// alternative that doesn't require users of Foo to
// create Bar
Foo(int s) : size(s), bar(Bar(14)) {}
You can use a default Bar object, which can be a static member variable of Foo or any other class that can provide such an object.
E.g.
class Foo {
private:
Bar& bar; // shared by other objects
int size;
//several other instance variables
static Bar& getDefaultBar();
public:
Foo(int s, Bar& b) : size(s), bar(b) {}
Foo(int s) : size(s), bar(getDefaultBar()) {}
}
Bar& Foo::getDefaultBar()
{
static Bar b(24);
return b;
}

Calling constant function from another class object

Here is the code that i have
class A
{
public:
void Func1() const;
};
class B
{
public:
A* a;
void Func2() const
{
// do something with 'a'
}
};
void A::Func1() const
{
B b;
b.a = this;
b.Func2();
}
Now obviously this is giving me an error in the line, because I'm trying to convert from const to non-const.
b.a = this;
Is there any way to call Func2 without having to cast away the constness of this. Since Func2 is a const function anyways, it will not change this.
You have to declare the A* as const:
class A
{
public:
void Func1() const;
};
class B
{
public:
const A* a;
void Func2() const
{
// do something with 'a'
}
};
void A::Func1() const
{
B b;
b.a = this;
b.Func2();
}
Since in A::Func1, the this pointer is const.
If class B is always going to work with *a as a const object then as others have said all it takes is to simply change the declaration to
public: const A* a
At this point I should mention that the constness of B::Func2 is a red herring because it has absolutely no relation to the constness of B::a. That B::Func2 is const means that it's not allowed to change the value of a; however, it is allowed to dereference a and mutate the resulting object.
Now, if class B has both const and non-const operations with respect to *a then your class design needs to change. It would be much better if you switched class B to use a const A* a as above, and added another class D : public B that encapsulates all the mutating operations. In addition, a should be hidden behind a property setter; this allows you to do things like
class B {
const A* a;
public:
void setA(const A* a) { this->a = a; }
void Func2() const {}
};
class D : public B {
A* a;
public:
using B::setA;
void setA(A* a) {
this->a = a;
static_cast<B*>(this)->setA(const_cast<const A*>(a));
}
void Func3() { /* do something to D::a */ }
};
With this scheme both B and D keep independent, suitably typed pointers to the object to be accessed. If setA is called on a B, or on a D with a const A* parameter then only B::a is set. If setA is called on a D with an A*, then both B::a and D::a are properly set. This has become possible because by abstracting the member behind a setter you can then overload the setter on the constness of its parameter.
Func2 may not change this, but b.a is not const and you're free to change it afterwards. There's no right way to do this, although workarounds exist, such as mutable or const_cast.
It's the sign of a faulty design.
Yes, make the A * constant:
class B {
public:
const A *a
...
};

How to return a const ref to a struct object such that all its nested structures also are read only?

How do I prevent all the member variables in a nested struct to be non modifiable?
typedef struct A
{
int a;
int b;
}A1;
typedef struct B
{
A1* objA;
}B1;
class C
{
public:
const B& GetB() const { return objB; }
PopulateB();
private:
B objB;
};
int main()
{
C objC;
objC.PopulateB();
const B& objB2 = objC.GetB();
objB2.objA->a = 3; // compiler allows this
}
I just want a struct that is completely read only and I expected this to work. (In this case objB2)
The pointer is const. The data it points to is not. Thats just how pointers work The best solution is to not expose raw pointers. A non-const accessor function would not be allowed on a const reference, so you can avoid the tricky indirection.
As an aside, you don't have to typedef structs like that. It's a C-ism.
The pointer type in your struct B refers to a non-const value, and this is the definition that counts. If you wanted to control access, you should hide data members and provide accessors and mutators:
struct B
{
private:
A1* objA;
public:
A1 & a1() {return *objA;}
A1 const & a1() const {return *objA;}
};
You can try:
typedef struct B
{
const A1& objA;
}B1;