Idiomatic way to prevent slicing? - c++

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

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.

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

Is removing this 2nd copy operation negligible

Am I correct in thinking that 2 copies of Bar are performed when creating an instance of Foo?
struct Foo {
Foo(Bar a) {
a = a; // 2nd copy Bar performed
}
private:
Bar a;
};
Foo f(myBar); // 1st copy of Bar performed
Would making the constructor parameter a constant reference reduce the number of copies down to one. Would modern compilers detect these 2 copies and optimise for me? So in effect, me trying to optimise is pointless as I already achieve that optimisation implicitly. Also would a constructor initialiser list result in anything different?
Ie;
struct Foo {
Foo(const Bar& a) {
a = a; // copy made
}
private:
Bar a;
};
Foo f(myBar); // no copy made
For the 1st case:
struct Foo {
Foo(Bar b) {
a = b; // copy assignment operator called
}
private:
Bar a; // default ctor called
};
Foo f(myBar); // copy ctor called
For the 2nd case:
struct Foo {
Foo(const Bar& b) {
a = b; // copy assignment operator called
}
private:
Bar a; // default ctor called
};
Foo f(myBar); // nothing
And you can improve it further with member initialize list.
struct Foo {
Foo(const Bar& b) : a(b) { // copy ctor called
}
private:
Bar a; // nothing
};
Foo f(myBar); // nothing
PS: I fixed the parameter name as #teivaz commented.

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

does assignment operator work with different types of objects?

class A {
public:
void operator=(const B &in);
private:
int a;
};
class B {
private:
int c;
}
sorry. there happened an error. is assignment operator valid ? or is there any way to achieve this? [There is no relation between A and B class.]
void A::operator=(const B& in)
{
a = in.c;
}
Thanks a lot.
Yes you can do so.
#include <iostream>
using namespace std;
class B {
public:
B() : y(1) {}
int getY() const { return y; }
private:
int y;
};
class A {
public:
A() : x(0) {}
void operator=(const B &in) {
x = in.getY();
}
void display() { cout << x << endl; }
private:
int x;
};
int main() {
A a;
B b;
a = b;
a.display();
}
This isn't an answer, but one should be aware that the typical idiom for the assignment operator is to have it return a reference to the object type (rather than void) and to return (*this) at the end. This way, you can chain the assignent, as in a = b = c:
A& operator=(const A& other)
{
// manage any deep copy issues here
return *this;
}
Both assignment operator and parameterized constructors can have parameters of any type and use these parameters' values any way they want to initialize the object.
Others have clued in on this, but I'll actually state it. Yes you can use different types, but note that unless you use friend, your class cannot access the private members of the class it's being passed in with the operator.
Meaning A wouldn't be able to access B::c because it's private.