Assignment operator on only one of two parents - c++

Child has two parents: Foo and Bar. Foo does not allow copying. Bar does. How can Child use Bar's assignment operator to copy into Bar's subset of Child (while leaving Foo's subset intact)?
To be more concrete: in the code below, how can Child refer to just Bar inside replace_bar()? (How would you modify line (X) to make the code compile?)
class Foo
{
public:
Foo () {}
private:
Foo (const Foo & f) {} // forbid copy construction
Foo & operator= (const Foo & foo) {} // forbid assignment
};
class Bar
{
public:
Bar () {}
Bar & operator= (const Bar & bar) {}
};
class Child : public Foo, public Bar
{
public:
Child () {}
void replace_bar (const Bar & bar2)
{
*this = bar2; // line (X)
}
};
int main ()
{
Child child;
Bar newbar;
child.replace_bar (newbar);
}

void replace_bar(const Bar& bar2) {
Bar::operator=(bar2);
}
In other news, you're missing a return *this; in Bar::operator= and if all you want to do is prevent copy of Foo you shouldn't define the copy constructor and assignment operator, only declare them. That way even if you try to use them from within the class you'll get an error (albeit a link error not a compile error).
class Foo {
Foo(const Foo&); // no body
Foo& operator=(const Foo&); // ditto
public:
Foo() { }
};

Related

C++ re-assignable const member variable

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.

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

How to assign the end of a reference chain?

For example I have a class:
class Foo {
public:
Foo(const Foo& foo) : father(foo) {}
private:
const Foo& father;
};
How to assign the father field if the object is the top?
I tried Foo foo(foo);, but the compiler warn me the foo is uninitialized, I guess the compiler only assign memory to the foo object after all initialization are done, so if I do this way, the father will reference to some wild memory address.
So, In this case, how to assign the father right if the object is the top?
Use a special constructor (and use a tag to distinguish your constructor from the copy constructor):
struct father_tag {};
class Foo {
public:
Foo(const Foo& foo, father_tag) : father(foo) {}
Foo() : father(*this) {}
private:
const Foo& father;
};
// usage:
Foo father;
Foo next(father, father_tag{});
or you can use a pointer instead of a reference, leaving it to nullptr at the end of the chain. Then you can check with if (father) if you are at the end or not:
class Foo {
public:
Foo(Foo const* pfather) : m_pfather(pfather) {}
Foo() : m_pfather(nullptr) {}
private:
Foo const* m_pfather;
};

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

copy constructor of a vector-derived class

I have the following classes.
class CA {
...
};
class CB: public vector<CA> {
...
};
How shall I implement the copy constructor of CB? i.e.,
CB(CB& cb).
How do I copy the content of the vector of cb in?
Ignoring the issue with publicly inheriting from an std::vector, if you really need a copy constructor for your derived type (for instance, if you are managing dynamically allocated resources), then you can use the constructor initialization list, as in this example, where Foo takes the place of your std::vector in the inheritance hierarchy:
#include <iostream>
struct Foo
{
Foo() {}
Foo(const Foo&) { std::cout << "Copy Foo\n";}
};
struct Bar : public Foo
{
Bar() {}
Bar(const Bar& b) : Foo(b) {}
};
int main()
{
Bar b1;
Bar b2(b1);
}