This is a very simple example with class A and B.
I want allow only deep copy, so I disabled the rvalue reference constructor.
#include <iostream>
class B;
class A {
public:
A();
~A();
A(const A & other);
A& operator=(const A & other);
A(A && ) = delete;
A& operator=(A && ) = delete;
B toB() const;
private:
int a_;
};
class B {
public:
B();
~B();
B(const B & other);
B& operator=(const B & other);
B(B && ) = delete;
B& operator=(B && ) = delete;
A toA() const;
private:
int b_;
};
A::A()
{
}
A::~A()
{
}
A::A(const A & other)
: a_(other.a_)
{
}
A& A::operator=(const A & other)
{
a_ = other.a_;
return *this;
}
B A::toB() const
{
return B();
}
B::B()
{
}
B::~B()
{
}
B::B(const B & other)
: b_(other.b_)
{
}
B& B::operator=(const B & other)
{
b_ = other.b_;
return *this;
}
A B::toA() const
{
return A();
}
int main()
{
A a();
B b();
return 0;
}
the gcc compiler report bugs like this:
In member function 'B A::toB() const':
error: use of deleted function 'B::B(B&&)'
return B();
^
note: declared here
B(B && ) = delete;
I'm wandering why it use B(B && ) function, not the B(const B &) function instead.
Because you added the move-constructor. Deleted but declared functions are still part of the interface of a class. That means it will be considered by the compiler, but since it's marked as deleted you get the error. If you want to force the copy-constructor to be called then remove the move-constructor declaration.
From this deleted functions reference:
Any use of a deleted function is ill-formed (the program will not compile). This includes calls, both explicit (with a function call operator) and implicit (a call to deleted overloaded operator, special member function, ...
[Emphasis mine]
Since the deleted functions are part of the interface, the compiler will try to use them. So when there is a deleted move constructor, the compiler will see that and try to use it, but since it is deleted there will be an error.
Since no move constructor will be created by the compiler if there is an explicit copy constructor (as per this move constructor reference) simply having a defaulted copy constructor will inhibit moving of object.
All of that means your classes can be very much simplified:
class A {
public:
A() : a_() {}
A(const A & other) = default;
B toB() const;
private:
int a_;
};
class B {
public:
B() : b_() {}
B(const B & other) = default;
A toA() const;
private:
int b_;
};
Because the classes above have user-declared copy constructors no movement constructor will be created, and the class can't be moved only copied.
Move Constructor would be called whenever a nameless object is created.
In B A::toB() const function there is a statement return B(); which creates a nameless object to be returned from the function. For creating a nameless object move constructor is required which is deleted.
Related
Is it possible to do a two-step initialization of a non-movable object in the member initializer list using C++17?
Here is the legacy API I’m working with (and yes I know it’s bad but I can’t change it)
class A {
public:
A(int some_param);
// no default, move or copy constructor nor assignment
A() = delete
A(const& A) = delete;
A(const&&) = delete; // it shouldn’t be deleted, but I’m not able to modify that class
A& operator= (const& A) = delete;
A& operator= (const&&) = delete;
// and it uses two phase construction
void init(int some_other_argument);
// more stuff
};
class B {
public:
// no default constructor, and must be created with a fully-initialized A
B() = delete;
B(const& A);
// more stuff
};
And here is what I am trying to write:
class C {
public:
C();
private:
A m_a;
B m_b;
};
C::C()
: m_a{ /* two step initialization of A*/}
, m_b(m_a)
{}
The usual trick of using a immediately initialize lambda doesn’t work:
C::C()
: m_a{[] {
auto a = A(0xdead_beef);
a.init(42);
return a; // doesn’t compile because A isn’t movable
}()}
, m_b(m_a)
{}
Nor does using the body of the constructor:
C::C()
// m_a and m_b will be default-initialized here, but their default initializer are deleted
{
m_a = A(0xdead_beef);
m_a.init();
m_b = B(m_a);
}
And nor doing the second step of the two step initialization in the body of the constructor:
C::C()
: m_a(0xdead_beef)
, m_b(m_a) // m_a isn’t fully initialized here
{
m_a.init();
}
Currently I’m using a unique_ptr for m_b, but I was asking myself if there was a better solution.
class C {
public:
C();
private:
std::unique_ptr<A> m_a; // guaranted to be initialized in the constructor, no need to check before dereferencing
B m_b;
};
C::C()
: m_a{[] {
auto a = new A(0xdead_beef);
a->init(42);
return a;
}()}
, m_b(*m_a)
{}
I think the rules of guaranteed move elision were improved in C++20, but I’m still using C++17.
You might still abuse of comma operator:
C::C() : m_a{ 0xdeadbeef },
m_b((m_a.init(42), m_a))
{}
template<typename T = uint8_t> class ArrayRef {
using uint = unsigned int;
protected:
ArrayRef() {}
ArrayRef(const ArrayRef&) {}
ArrayRef& operator=(const ArrayRef& other) { return *this; }
};
class ByteArray : ArrayRef<uint8_t> {
ByteArray(const ArrayRef&);
ByteArray& operator=(const ArrayRef&);
public:
ByteArray() {}
};
class Base {
using uint = unsigned int;
protected:
Base() {}
Base(const Base&) {}
Base& operator=(const Base& other) { return *this; }
};
class Derived : Base {
Derived(const Derived&);
Derived& operator=(const Derived& other) { return *this; }
public:
Derived() {}
};
int main() {
ByteArray ba;
ByteArray ba2 = ba; // no error, why?
ba = ba2; // no error why?
Derived d;
Derived d2 = d; // error (expected) - Calling a private constructor
d = d2; // error (expected) - Calling private assignment operator
}
Two questions about the code above.
Can you explain why the templated code behaves differently than the non-templated code? (see the comments in main().
How would I go about creating private copy constructors and assignment operators properly for templated code like this, in order to prevent object copies?
Can you explain why the templated code behaves differently than the non-templated code? (see the comments in main().
The difference does not have anything to do with templates.
The difference is that your copy constructor and copy assignment operator are implicitly defined (as public) in ByteArray. In Derived you've made them private. If you take the template out of the question, it may be easier to see the difference between your two versions:
class ArrayRef {
protected:
ArrayRef() {}
ArrayRef(const ArrayRef&) {}
ArrayRef& operator=(const ArrayRef& other) { return *this; }
};
class ByteArray : ArrayRef {
ByteArray(const ArrayRef&); // your converting ctor
ByteArray& operator=(const ArrayRef&); // your converting assignment op
public:
ByteArray() {}
// copy contructor - implicitly defined:
// ByteArray(const ByteArray&) = default;
// copy assignment operator - implicitly defined:
// ByteArray& operator=(const ByteArray&) = default;
};
If you now try copying a ByteArray it will work just as fine as when it was based on an instance of a class template. Compare the above ByteArray with your Derived in which you've actually made the copy constructor and copy assignment operator private.
How would I go about creating private copy constructors and assignment operators properly for templated code like this, in order to prevent object copies?
In order to prevent copies, you may delete them:
class ByteArray : private ArrayRef<uint8_t> {
public:
ByteArray(const ByteArray&) = delete;
ByteArray& operator=(const ByteArray&) = delete;
ByteArray() {}
};
or make them private to allow ByteArrays and friends to make copies:
class ByteArray : private ArrayRef<uint8_t> {
private:
ByteArray(const ByteArray&) { ... }; // or `= default`
ByteArray& operator=(const ByteArray&) { ...; return *this; } // or `= default`
public:
ByteArray() {}
};
Note that the private (converting) constructor and (converting) assignment operator that takes a const ArrayRef<uint8_t>& as input does not prevent the implicitly defined copy constructor and copy assignment operator that takes a const ByteArray& as input from behing created in ByteArray.
In your Derived class, you've actually made the copy constructor and copy assignment operator private which is why you get the expected compilation errors when trying to use those.
Here's a full example that includes your (converting) constructor and assignment operator as well as a (user defined) copy constructor and copy assignment operator.
#include <cstdint>
#include <iostream>
template<typename T = uint8_t> class ArrayRef {
protected:
ArrayRef() {}
ArrayRef(const ArrayRef&) {}
ArrayRef& operator=(const ArrayRef& other) { return *this; }
};
class ByteArray : ArrayRef<uint8_t> {
ByteArray(const ArrayRef&) { std::cout << "your converting ctor\n"; }
ByteArray& operator=(const ArrayRef&) {
std::cout << "your converting assignment op\n"; return *this;
}
public:
ByteArray(const ByteArray&) { std::cout << "copy ctor\n"; }
ByteArray& operator=(const ByteArray&) {
std::cout << "copy assignment op\n"; return *this;
}
ByteArray() {}
};
int main() {
ByteArray a;
ByteArray b = a;
a = b;
}
You can see in the output that none of your private methods are used:
copy ctor
copy assignment op
Just failed an interview because of this, and I'm still confused:
class A
{
public:
A(int a) : a_(a) {}
// Copy constructor
// Assignment operator
private:
int a_;
};
class B : public A
{
public:
B(int a, int b) : A(a), b_(b) {
}
// Copy constructor
// Assignment operator
private:
int b_;
};
How do you implement the copy constr/assignment op, and why does the constructor of B have to have an A initialized in the initializer list? It doesn't work if it's not in the list.
How do you implement the copy constr/assignment op
Trick question, I think. In this case the best option is to do absolutely nothing. Neither class contains (and owns) any resources requiring more than the default special member functions. Observe the Rule of Zero.
Some style guides recommend explicitly defaulting special member functions. In this case
class A
{
public:
A(int a) : a_(a) {}
A(const A &) = default;
A& operator=(const A &) = default;
private:
int a_;
};
may be appropriate.
why does the constructor of B have to have an A initialized in the initializer list?
All class members and base classes must be fully constructed before entering the body of a constructor.
B(int a, int b)
{ // Entering constructor's body. A must be constructed before we get here.
}
A has no default constructor, so the constructor it does have must be explicitly called in the initializer list to construct the base class before the construction of B can proceed.
Addressing comment
A's copy constructor and assignment operator are trivial:
A(const A & src): a_(src.a_)
{
}
A& operator=(const A & src)
{
a_ = src.a_;
return *this;
}
B is a bit trickier because it also has to make sure A is copied
B(const B & src): A(src), // copy the base class
b_(src.b_)
{
}
B& operator=(const B & src)
{
A::operator=(src); // assign the base class by explicitly calling its
// assignment operator
b_ = src.b_;
return *this;
}
Note: If I were hiring, I'd take the programmer who called me out on the trick question over the programmer who did the extra work and risked an unforced error.
why does the constructor of B have to have an A initialized in the
initializer list?
A only has one constructor which takes an int. B has A as a base class, which means that every B object must first construct an A object. How else are you going to construct that A object with it's required parameter except by using an initialiser list?
According to CppCoreGuideline, I should disable the copy constructor of a base class and propose a clone method: https://github.com/isocpp/CppCoreGuidelines/blob/master/CppCoreGuidelines.md#Rc-copy-virtual
For example:
class B {
public:
explicit B() = default;
B(B&&) = default; // Move constructor
B& operator=(B&&) = default; // Move assignment operator
B(const B&) = delete; // Copy constructor
B& operator=(const B&) = delete; // Copy assignment
virtual ~B() = default;
virtual unique_ptr<B> clone()
{
return unique_ptr<B>{new B{*this}}; // how do this without copy constructor ?
}
private:
int c;
int d;
};
class D : public B {
public:
explicit D() = default;
D(D&&) = default; // Move constructor
D& operator=(D&&) = default; // Move assignment operator
D(const B&) = delete; // Copy constructor
D& operator=(const D&) = delete; // Copy assignment
virtual ~D() = default;
virtual unique_ptr<B> clone() override
{
// how can I copy all private data member of base class ???
}
};
but how can I copy all private data member in clone method? Obviously I'll use the CRTP pattern : C++: Deep copying a Base class pointer
I think the simplest way is to actually make the special members protected instead of deleted. This still prevents slicing, but makes it easier to implement clone(). Note that both the copy and move members need to be treated this way.
class B {
public:
// if this is truly intended to be a polymorphic base class, it probably
// doesn't make sense for the base to be able to clone itself.
virtual unique_ptr<B> clone() = 0;
protected:
B(B const& ) = default;
B& operator=(B const& ) = default;
private:
int c;
int d;
};
Which also allows the derived classes to do this easily:
class D : public B {
public:
D(D const& ) = default; // this is safe now
D& operator=(D const& ) = default;
unique_ptr<B> clone() override {
return unique_ptr<D>(new D(*this));
}
// ...
};
Rather than disabling the copy constructor, consider marking it protected. That way, clients of the class can't accidentally create a copy, but instances of the class can invoke the copy constructor as needed to implement the clone function. You can use the defaulted version of the copy constructor assuming you aren't doing any explicit resource management. Then, to implement clone, you can do something like this:
virtual unique_ptr<B> clone() override
{
return make_unique<D>(*this);
}
This invokes the object's own (protected) copy constructor, which in turn will invoke the base's (protected) copy constructor, etc.
As a note, there's no need to use CRTP here. Using good old fashioned copy constructors should be all you need.
I have a surprising issue in VS2010.
This is the header.h of my main.cpp
class A;
class B;
class A
{
public:
double x,y;
A();
~A();
A(const A &obj);
A(const B &obj);
A& operator=(const A &obj);
};
class B
{
public:
double x,y;
B();
~B();
B(const B &obj);
B& operator=(const B &obj);
};
The main.cpp is containing the declaration of methods and :
#include "header.h"
#include <iostream>
int main() {
A t;
B u;
A a(u);
t=u;
return 0;
}
As you can see, to do
A a(u);
I had to add this method
A(const B &obj);
But for
t=u;
It uses
A& operator=(const A &obj);
Why I don't get an error ?
If you want to avoid this, you should mark your "A from B" constructor as explicit:
explicit A(const B &obj);
Any constructor not marked as explicit will be used by the compiler for implicit conversions.
by adding A(const B &obj); you provided a way to convert B to A, which is used in t=u; so the compiler can do what you ask and no error is needed. The conversion can be done because the parameter const A &obj allows conversion.
If, for whatever reason, the A(const B &obj) is meaninful but you also want t=u to fail then you have to disable implicit type conversion using the explicit keyword.
This means that only when you specifically cast B as an instance of A will the conversion be performed.