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
Related
In code
class A
{
public:
A() = default;
A(const A& obj) = default;
};
since i define the copy constructor of A, A won't synthesize the move constructor. But i still can write the following code:
A obj;
A obj1 = std::move(obj); // call the copy constructor of A
But if i define class A like this
class A
{
public:
A() = default;
A(const A& obj) = default;
A(A&& obj) = delete;
};
the same code
A obj;
A obj1 = std::move(obj); // error: call to deleted constructor of A
then how to understand the difference between cannot synthesize a move constructor and the move constructor is deleted?
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.
I recently read about copy & swap and am now trying to implement the ctors in a base and derived class. I have the four constructors in both my base and derived class, however I am unsure how to implement the assignment operator of the derived class.
explicit Base(int i) : m_i{i} {}
Base(const Base & other) : m_i{other.m_i}
Base(Base && other) : Base(0) { swap(*this, other); }
Base & operator=(Base other) { swap(*this, other); return *this; }
friend void swap(Base & a, Base & b) noexcept {
using std::swap;
swap(a.m_i, b.m_i);
}
explicit Derived(int j) : Base(42), m_j(j) {}
Derived(const Derived & other) : Derived(other.m_j) {}
Derived(Derived && other) : Derived(other.m_j) { swap(*this, other); }
Derived & operator=(Derived other) { /*???*/ }
friend void swap(Derived & a, Derived & b) noexcept {
using std::swap;
swap(a.m_j, b.m_j);
}
Consider using = default as much as possible. And if we are talking about public inheritance, you really need a virtual destructor as well.
Here is how your Base would look using the copy/swap style:
class Base
{
int m_i;
public:
virtual ~Base() = default;
Base(const Base& other) = default;
Base& operator=(Base other) noexcept
{
swap(*this, other);
return *this;
}
Base(Base&& other) noexcept
: Base(0)
{
swap(*this, other);
}
explicit Base(int i) noexcept
: m_i{i}
{}
friend void swap(Base& a, Base& b) noexcept
{
using std::swap;
swap(a.m_i, b.m_i);
}
};
The only difference from what you have is that I've added the virtual destructor, and used = default for the copy constructor.
Now for Derived:
class Derived
: public Base
{
int m_j;
public:
Derived(const Derived& other) = default;
Derived& operator=(Derived other) noexcept
{
swap(*this, other);
return *this;
}
Derived(Derived&& other) noexcept
: Derived(0)
{
swap(*this, other);
}
explicit Derived(int j) noexcept
: Base(42)
, m_j{j}
{}
friend void swap(Derived& a, Derived& b) noexcept
{
using std::swap;
swap(static_cast<Base&>(a), static_cast<Base&>(b));
swap(a.m_j, b.m_j);
}
};
I've let the compiler implicitly take care of the destructor since the compiler will implicitly give me a virtual one that does the right thing in this case.
Again I've explicitly defaulted the copy constructor. This corrects a bug in your version which neglects to copy Base.
The operator= looks just like the Base version.
The Derived move constructor does not need to move or copy anything from other since it is going to swap with other.
The Derived swap function must swap the Base part as well as the Derived part.
Now consider not using the copy/swap idiom. This can be surprisingly easier, and in some cases, higher performing.
For Base you can use = default for all 5 of your special members:
class Base
{
int m_i;
public:
virtual ~Base() = default;
Base(const Base&) = default;
Base& operator=(const Base&) = default;
Base(Base&&) = default;
Base& operator=(Base&&) = default;
explicit Base(int i) noexcept
: m_i{i}
{}
friend void swap(Base& a, Base& b) noexcept
{
using std::swap;
swap(a.m_i, b.m_i);
}
};
The only work that is really required here is your custom constructor and swap function.
Derived is even easier:
class Derived
: public Base
{
int m_j;
public:
explicit Derived(int j) noexcept
: Base(42)
, m_j{j}
{}
friend void swap(Derived& a, Derived& b) noexcept
{
using std::swap;
swap(static_cast<Base&>(a), static_cast<Base&>(b));
swap(a.m_j, b.m_j);
}
};
All 5 of the special members can be implicitly defaulted!
We couldn't default them in the Base because we needed to specify the virtual destructor, which inhibits the generation of the move members, and the generation of the copy members is deprecated with a user-declared destructor. But since we do not need to declare the destructor in Derived, we can just let the compiler handle everything.
As one of the big selling points of copy/swap is reduced coding, it can be ironic that using it can actually require more coding than letting the compiler default the special members.
Of course if the defaults do not do the right thing, then don't use them. I'm simply saying that the defaults should be your first choice, ahead of copy/swap.
You implement op= exactly the same way for Derived as for Base:
Derived& operator=(Derived other) { swap(*this, other); return *this; }
I hope you are aware of the up- and down-sides of passing the argument by value there, though:
Up-side: Only one function needed for all value categories.
Down-Side: Second move for xvalues, move in addition to the needed copy for prvalues.
Other points to consider:
Rule-of-thumb: Single-argument non-copy/move ctors should be explicit: You really don't want to have an implicit conversion from int to Base...
You forgot to re-implement swap for Derived (swap all sub-objects, both base and member). You might forego it if Derived does not add any members though.
How can I make a Class non-cloneable like we can do in Java while creating singleton.
Is there any condition we can put on copy constructor so that an exception can be thrown if user tries to make a copy of an object?
I am a novice in C++ so kindly add any info to this or redirect if an answer is already available for the same.
Just declare copy constructor and copy assign operator private
in C++03
class NonCopyable
{
public:
NonCopyable() { }
private:
NonCopyable(const NonCopyable&);
NonCopyable& operator=(const NonCopyable&);
};
Also you can make a class derive from NonCopyable, AnotherType is un-copyable
class AnotherNonCopyable : private NonCopyable
{
public:
AnotherNonCopyable () {}
}
With C++11:
class NonCopyableType
{
public:
NonCopyableType(const NonCopyableType&) = delete;
NonCopyableType& operator=(const NonCopyableType&) = delete;
};
You can delete the copy constructor and assignment operator:
struct Foo
{
Foo(const& Foo) = delete;
Foo& operator=(const Foo&) = delete;
};
If you don't have C++11 support, make them private, and don't implement them:
struct Foo
{
private:
Foo(const& Foo);
Foo& operator=(const Foo&);
};
Note In C++, class and struct are essentially the same.
Declare the copy-semantics as delete:
//C++11 only
MyClass(MyClass const &) = delete;
MyClass& operator=(MyClass const &) = delete;
That makes the class non-copyable!
//pre-C++11 code
private:
MyClass(MyClass const &); //declare only, don't define it
MyClass& operator=(MyClass const &); //declare only, don't define it
This should also work!
Is there any condition we can put on copy constructor so that an
exception can be thrown if user tries to make a copy of an object.
if you make the copy constructor private, The code will not compile when the programmer tries to make another copy. This is probably better than detecting the error with an exception at runtime.
class foo {
private:
operator = ( const foo& f);
};
Trying to resolve error C2248 related to abstract base class using implementation of copy/move ctors/assignment operators and dtor (Rule of Five) and a few questions come up:
1) Why does the rule of 5, primarily relating to the dtor, apply when the unique_ptr data members are handled automatically? The dtor implementation should be left empty correct, since the unique_ptrs are automatically destroyed once their owners go out of scope?
2) Suppose another class had a member of type std::unique_ptr of a vector of the same type. In order for this class to be copyable, it must have a copy ctor and copy assignment operator that clone the unique_ptr data member? I have seen this solution, but is seems like the original poster just switched over to shared_ptr for the sake of removing the error alone with little consideration of ownership management. Is this the correct strategy?
3) Consider the same case as question 2 above relating to vector of unique_ptr. Should dtor include a call to clear() the vector?
4) The assignment operators for the Derived1 are not correct. But the base class is supposed to have copy and move assignment operators, since it has copy/move ctors (rule of 4/5). These can't actually be used outside of the class since it is abstract and thus no instances will be assigned. But how do I utilize this code from the derived classes? Each derived class needs to be able to move/copy the base data members and it's own data members. I'm not sure what to do.
#include <algorithm>
#include <memory>
#include <vector>
#include <iostream>
class Base{
public:
Base() : m_subBases(){};
/* copy ctor */
Base(const Base& other) : m_subBases(){
*this = other;
};
/* move ctor */
Base(Base&& other) : m_subBases(){
*this =std::move( other);
};
/* Move assignment operator*/
Base& operator=(Base&& other){
m_subBases = std::move(other.m_subBases);
return *this;
};
/* Copy assignment operator */
Base& operator=(const Base& other){
for(int i = 0; i < other.m_subBases.size(); i++)
m_subBases.push_back(other.m_subBases[i]->clone());
return *this;
};
/* virtual dtor */
virtual ~Base(){
m_subBases.clear();
};
/* Used for creating clones of unique_ptrs */
virtual std::unique_ptr <Base> clone() const= 0;
/* Do something */
virtual void execute(float f) = 0;
//Omitted data member access methods
protected:
std::vector < std::unique_ptr <Base> > m_subBases;
};
class Derived1 : public Base{
public:
Derived1() : Base(){};
/* copy ctor */
Derived1(const Derived1& other) : Base(other){
*this = other;
};
/* move ctor */
Derived1(Derived1&& other) : Base(std::move(other)){
*this = std::move(other);
};
/* Move assignment operator*/
Derived1& operator=(Derived1&& other){
//This is redundant when called in the move ctor because
// of the call to Base(std::move(other))
m_subBases = std::move(other.m_subBases);
m_string = other.m_string;
return *this;
};
/* Copy assignment operator */
Derived1& operator=( const Derived1& other){
//This is redundant when called in the copy ctor because
// of the call to Base(other)
for(int i = 0; i < other.m_subBases.size(); i++)
m_subBases.push_back(other.m_subBases[i]->clone());
m_string = other.m_string;
return *this;
};
/* virtual dtor */
virtual ~Derived1(){};
/* Used for creating clones of unique_ptrs */
virtual std::unique_ptr <Base> clone() const{
return std::unique_ptr <Base> (new Derived1(*this));
};
virtual void execute(float f){
std::cout << "Derived1 " << f << std::endl;
};
protected:
std::string m_string;
};
I'd like to offer an alternative approach. Not the Scary Rule of Five, but the Pleasant Rule of Zero, as #Tony The Lion has already suggested. A full implementation of my proposal has been coded by several people, and there's a fine version in #R. Martinho Fernandes's library, but I'll present a simplified version.
First, let's recap:
The Rule of Zero: Don't write a copy- or move-constructor, a copy- or move-assignment operator, or a destructor. Instead, compose your class of components which handle a single responsibility and encapsulate the desired behaviour for the individual resource in question.
There's an obvious caveat: When you design the single-responsibility class, you must of course obey:
The Rule of Five: If you write any one of copy- or move-constructor, copy- or move-assignment operator, or destructor, you must implement all five. (But the "five" functions needed by this rule are actually: Destructor, Copy-Const, Move-Const, Assignment and Swap.)
Let's do it. First, your consumer:
struct X;
struct Base
{
std::vector<value_ptr<X>> v;
};
struct Derived : Base
{
};
Note that both Base and Derived obey the Rule of Zero!
All we need to do is implement value_ptr. If the pointee is non-polymorphic, the following will do:
template <typename T>
class value_ptr
{
T * ptr;
public:
// Constructors
constexpr value_ptr() noexcept : ptr(nullptr) { }
constexpr value_ptr(T * p) noexcept : ptr(p) { }
// Rule of Five begins here:
~value_ptr() { ::delete ptr; }
value_ptr(value_ptr const & rhs) : ptr(rhs.ptr ? ::new T(*rhs.ptr) : nullptr) { }
value_ptr(value_ptr && rhs) noexcept : ptr(rhs.ptr) { rhs.ptr = nullptr; }
value_ptr & operator=(value_ptr rhs) { swap(rhs); return *this; }
void swap(value_ptr & rhs) noexcept { std::swap(rhs.ptr, ptr); }
// Pointer stuff
T & operator*() const noexcept { return *ptr; }
T * operator->() const noexcept { return ptr; }
};
template <typename T, typename ...Args>
value_ptr<T> make_value(Args &&... args)
{
return value_ptr<T>(::new T(std::forward<Args>(args)...));
}
If you would like smart pointer that handles polymorphic base class pointers, I suggest you demand that your base class provide a virtual clone() function, and that you implement a clone_ptr<T>, whose copy constructor would be like this:
clone_ptr(clone_ptr const & rhs) : ptr(rhs.ptr ? rhs.ptr->clone() : nullptr) { }