Copy assignment operator defined in template being deleted by compiler - c++

I'm familiar with the principle (for example, from this answer and this one) that when a class has a move constructor and/or move assignment operator, its default copy constructor and copy assignment operator are deleted. However, In the examples I've seen, this can be addressed by explicitly defining a new copy constructor and assignment operator.
In my particular case, I have a class which is derived by joint inheritance from a C-style struct and a template class. The copy and move assignment operators are explicitly defined in the template, whilst the copy and move constructors are explicitly defined in the class itself. In other words, everything is defined explicitly, though not all in the same place. Here is some example code:
typedef struct {
int n;
} myStruct;
template <typename T> class myTemplate
{
public:
// Default constructor
myTemplate<T>() : t_n(nullptr) {}
// Cannot create copy or move constructors in template, as cannot
// access the 'n' member directly
// Copy assignment operator
myTemplate<T> & operator=(const myTemplate<T> &source)
{
if (this != &source)
{
*t_n = *(source.t_n);
}
return *this;
}
//! Move assignment operator
myTemplate<T> & operator=(myTemplate<T> &&source)
{
if (this != &source)
{
*t_n = *(source.t_n);
*(source.t_n) = 0;
source.t_n = nullptr;
}
return *this;
}
T* t_n;
};
class myClass : public myStruct, public myTemplate<int>
{
public:
// Default constructor
myClass() : myTemplate<int>()
{
n = 0;
t_n = &n;
}
// Alternative constructor
myClass(const int &n_init) : myTemplate<int>()
{
n = n_init;
t_n = &n;
}
// Copy constructor
myClass(const myClass &source) : myTemplate<int>()
{
n = source.n;
t_n = &n;
}
// Move constructor
myClass(myClass &&source) : myTemplate<int>()
{
n = source.n;
t_n = &n;
source.n = 0;
source.t_n = nullptr;
}
};
int main()
{
myClass myObject(5);
myClass myOtherObject;
// Compilation error here:
myOtherObject = myObject;
return 1;
}
In Visual C++ and Intel C++ on Windows, this works exactly as I'd expect it to. On gcc 4.9.0 in Linux, however, I get the dreaded error message:
g++ -c -std=c++11 Main.cppMain.cpp: In function ‘int main()’:
Main.cpp:78:19: error: use of deleted function ‘myClass& myClass::operator=(const myClass&)’
myOtherObject = myObject;
^
Main.cpp:39:7: note: ‘myClass& myClass::operator=(const myClass&)’ is implicitly declared as deleted because ‘myClass’ declares a move constructor or move assignment operator
class myClass : public myStruct, public myTemplate<int>
Sure enough, the error goes away if I define an explicit copy assignment operator in the class itself, rather than in the template, but that's bothersome to do and undermines the advantage of using the template, since (a) my actual copy assignment operator is a lot bigger than the one shown here and (b) there are a large number of different classes that all share this template.
So, is this simply a bug in gcc 4.9.0, or is this in fact what the standard says should happen?

GCC is correct (and Clang and EDG agree).
myTemplate has a user-declared move constructor, therefore its copy assignment operator is deleted.
You've provided a copy constructor, but not copy assignment operator. Just declare a copy assignment operator for myTemplate and define it as defaulted. That takes one extra line of code.

Related

Use assignment operators instead of an implicit constructor

In my program, I am trying to use the assignment operator= to assign an object of my class. I am specifically trying to call the assignment operator instead of an implicit constructor (thus the explicit keyword). When I try to compile, I get a C2440 Compiler Error:
class MyClass {
public:
explicit MyClass(double x = 0.) :
m_x(x) {
}
MyClass& operator = (const double& other) {
m_x = other;
/* do something special here */
return *this;
}
private:
double m_x;
};
int main()
{
MyClass my_class = 2.; // C2440
return 0;
}
I guess that the compiler fails trying to call the constructor implicitly (because of explicit). Does anyone have a fix?
MyClass my_class = 2.; is more or less equivalent to MyClass my_class(2.);, but, you marked the constructor as explicit, which prevents C++ from doing that automatically.
So, in the way you've written your code, you cannot really do what you want. You could explicitly call the constructor using:
MyClass my_class(2.); // Explcitly call your constructor
Or, as Ted Lyngmo mentioned in the comments, you can do:
MyClass my_class; // Call your constructor with default argument of 0
my_class = 2.; // then call your assignment operator

Copy Constructors, Assignment Operator C++

I am very new to OOP and am still trying to comprehend all the concepts of the constructors. I have a class with some data and I have to make a Copy Constructor and Assignment Operator, however, since this is the first time I am doing something like this, I am not sure if what I have written makes sense. So, I am asking if what I have written are valid Copy Constructor and Assignment Operator. The class is saved in a file called BKS.h Thank you!
Here is the class :
#include <iostream>
#include <vector>
#include <cassert>
#include <algorithm>
using namespace std;
template <class T>
class BKS final
{
public:
struct Item
{
T identifier;
int weight;
int benefit;
};
BKS() {}
BKS(const BKS<T> &copy);
BKS(const vector<Item> &items) : itemlist_{items} {}
BKS(const vector<pair<int, int>> &weight_benefit_list);
BKS<T> &operator=(const BKS<T> &copy);
// some methods ....
private:
vector<Item> itemlist_;
vector<int> current_selection_;
int current_capacity_ {0};
int maximal_benefit_ {0};
};
Copy Constructor and Assigment Operator :
#include "bks.h"
template <class T>
BKS<T>::BKS(const BKS<T> &copy) // copy constructor
{
std::vector<Item> itemlist_ = copy.itemlist_;
std::vector<int> current_selection_ = copy.current_selection_;
int current_capacity_ = copy.current_capacity_;
int maximal_benefit_ = copy.maximal_benefit_;
}
template <class T>
BKS<T> &BKS<T>::operator=(const BKS<T> &copy)
{
if (&copy != this)
{ // check for self-assignment
this->itemlist_ = copy.itemlist_;
this->current_selection_ = copy.current_selection_;
this->current_capacity_ = copy.current_capacity_;
this->maximal_benefit_ = copy.maximal_benefit_;
}
return *this;
}
Also any general recommendations concerning constructors are welcome :)
If your instructor insists you must declare special members, but gives no guidance on how, then the best way is:
template <class T>
class BKS final
{
public:
~BKS() = default;
BKS(const BKS &) = default;
BKS& operator=(const BKS &) = default;
BKS(BKS &&) = default;
BKS& operator=(BKS &&) = default;
/* other members... */
};
If your instructor doesn't require you to declare them, but only requires they exist, the best way is
template <class T>
class BKS final
{
public:
/* other members... */
};
When you create a new class without declaring a copy constructor, a copy assignment operator and a destructor, compilers will declare their own versions of a copy constructor, a copy assignment operator and a destructor. Furthermore, if you don't declare your constructor, the compilers will implicitly declare one for you.
All these default compiler generated functions are all public and inline. Notice that the implictly-declared destructor is non-virtual.
So what will the default constructor, default copy constructor, default copy assignment operator and default destructor do?
Default constructor (either implicitly-declared or user-defined): calls the default constructors of the bases and of the non-static members of the class;
Default copy constructor(implicitly-declared) and default copy assignment operator(implicitly-declared): copy each non-static data member of the source object to the target object;
Default destructor(either implicitly-declared or user-defined): calls the destructors of the base class and members of the derived class.
Here in your cases, your non-static data members will be copied by default copy constructor and default copy assignment operator. There's no need defining your own copy constructor and copy assignment operator unless you want to add specific behaviors for your copy constructor and copy assignment operator.
References:
https://en.cppreference.com/w/cpp/language/default_constructor
https://www.ibm.com/support/knowledgecenter/SSLTBW_2.3.0/com.ibm.zos.v2r3.cbclx01/cplr380.htm
Book: "Effective C++" by Scott Meyers, Chapter 2

Template syntax to accept an argument with the type of the derived object

I have a class template in which there is a function that takes an argument of the type of the class that inherits the template. I can't seem to find the right syntax to bring this about. For example, if this is my template:
// A template to promote a simple C-style struct, T_C,
// to a C++ class 'T' with constructors, destructors,
// copy and move assignment operators.
template <typename T_C> class myTemplate
{
// We could in principle create templated copy and move
// constructors and assignment operators, but they'd be
// implicitly deleted by the compiler unless we explicitly defaulted
// them: see
// http://stackoverflow.com/questions/25246573/copy-assignment-operator-defined-in-template-being-deleted-by-compiler.
// Explicitly defaulting them works well if the template is
// contained in an all-header library. However, on including
// the template-derived classes into a DLL in Visual Studio 2013
// it was found that, even when all the other methods
// in a class were properly exported, the defaulted
// template methods were not. There may be a workaround for
// this, but in the scheme of things it was thought easier
// just to dispense with the templated operators and to
// create copy and move assignment operators, as well as the
// corresponding constructors, for each derived type.
//
// We can at least simplify things a little, and force some
// consistency upon our various class definitions,
// by insisting that every relevant class defines the
// functions listed, which can then be called from the
// constructors and assignment operators to make them a bit
// more manageable.
// Tidy up all pointers etc. and return all buffers to a safe state
virtual void clear() = 0;
protected:
// Construct a 'T' from a T_C:
virtual void construct_contents(const T_C &source) = 0;
// Deep copy the contents of a T_C to a 'T'
virtual void copy_contents(const T_C &source) = 0;
// Move the contents of one object to another: for use in both the move constructor
// and the move assignment operator:
virtual void move_contents(myTemplate<T_C> &&source) = 0;
// For sure this is wrong, but I can't figure out
// what the right argument type should be
};
...and this is my class
class myClass : public myStruct, public myTemplate<myStruct>
{
public:
// Default constructor
myClass() {}
// Copy constructor taking basic C struct
myClass(const myStruct &source)
{ construct_contents(source); }
// Copy constructor taking this promoted C++ class
myClass(const myClass &source)
{ construct_contents(source); }
// Copy assignment operator taking basic C struct
MyClass & operator=(const myStruct &source)
{
copy_contents(source);
return *this;
}
// Copy assignment operator taking this promoted C++ class
MyClass & operator=(const myClass &source)
{
copy_contents(source);
return *this;
}
// Move constructor taking this promoted C++ class
myClass(myClass &&source)
{
move_contents(std::move(source));
}
// Move assignment operator taking this promoted C++ class
myClass & operator=(myClass &&source)
{
if (this != &source)
{
clear();
move_contents(std::move(source));
}
return *this;
}
// Destructor
~myClass()
{
clear();
}
// Various getters and setters for the data fields of myStruct
// ....
virtual void clear() override
{
// Stuff...
}
protected:
virtual void construct_contents(const myStruct &source) override
{
// Stuff...
}
virtual void copy_contents(const myStruct &source) override
{
// Stuff...
}
virtual void move_contents(myClass &&source) override
{
// Stuff...
}
};
...then everything works just fine except that the compiler throws an error saying (in Visual Studio 2013) 'member function declared with "override" does not override a base class member' when it encounters my definition of myClass::move_contents. I can understand why it is unhappy: 'myClass' is not only a 'myTemplate<myStruct>', but a type that jointly inherits both myTemplate<myStruct> and myStruct. What I actually need to do is to declare move_contents in the template so that the argument type is the same as the type of the derived object, in this case 'myClass'. However, I can't think of a way of specifying this in the template declaration.
My working solution is to take move_contents out of the template altogether and define a totally independent move_contents method for each separate derived type. This is all right, but doing this would block off the possibilty of pulling the move constructor and assignment operator back into the template - something I'd love to do if I can ever figure out a way of getting Visual Studio 2013 to export them properly.
The template parameter is myStruct, so the method expects an argument of type myTemplate<myStruct>, not myClass.
Declare your method with :
virtual void move_contents(myTemplate<myStruct> &&source) override
{
// Stuff...
}
I'm going to accept #quantdev's answer because he put me on the right track, but here is what eventually worked.
In my original code snippet (see my question above), I inserted an additional public virtual function into the template:
virtual T_C * baseObject() = 0;
In my definition of myClass, I overrode this as follows:
virtual myStruct * baseObject() override { return static_cast<myStruct *>(this); }
This worked because myClass inherits myStruct, so the 'this' pointer of any myClass object is always castable to a myStruct*.
Then, in my move_contents method, I was able to access the myStruct pointers to both the source and destination objects:
void move_contents(myTemplate<myStruct> && source) override
{
stuff = source.baseObject() -> stuff;
source.baseObject() -> stuff = nullptr;
// So that the buffer which my new object now
// points to isn't released when the source
// object is destroyed
}
As far as I can see, this does everything that I need.

What operators to implement with int members

I have a class that has a few int and a const int member with a constructor defined.
class SomeContainer
{
public:
SomeContainer():
member1(0),
member2(staticMethod())
{}
private:
static int staticMethod();
int member1;
const int member2;
}
I need to create an assignment operator since I use this class in another class MainClass and code
MainClass* p1;
MainClass* p2
{
//...
*p1 = *p2 // fails since SomeContainer doesn't have copy assignment operator
}
Should this code be enough or am I missing anything?
{
SomeContainer(const SomeContainer& other): // copy ctor
member1(other.member1),
member2(other.member2)
{}
SomeContainer& operator=(const SomeContainer& other) // assignment operator
{
member1 = other.member1;
}
}
what about move ctor and move assignment? Should I need to implement those as well?
First of all if p1 and p2 are pointers then you can just assign them.
In case move constructor is not present then copy constructor will be used instead.
It remains for me hard to understand what is the logical meaning of allowing assignment on something that has constant non-static parts.
What you have written is sufficient.
When no move constructor exists then the copy constructor will be used instead, and there's really no purpose in move semantics here.
Just remove const from that member if you really need assignment

How Many default methods does a class have?

Sorry, this might seem simple, but somebody asked me this, and I don't know for certain.
An empty C++ class comes with what functions?
Constructor,
Copy Constructor,
Assignment,
Destructor?
Is that it? Or are there more?
In C++03 there are 4:
Default constructor: Declared only if
no user-defined constructor is
declared. Defined when used
Copy constructor - declared only if the user hasn't declared one. Defined if used
Copy-assignment operator same as above
Destructor same as above
In C++11 there are two more:
Move constructor
Move-assignment operator
It is also possible that the compiler won't be able to generate some of them. For example, if the class contains, for example, a reference (or anything else that cannot be copy-assigned), then the compiler won't be able to generate a copy-assignment operator for you. For more information read this
If I define the following class
class X
{};
The compiler will define the following methods:
X::X() {} // Default constructor. It takes zero arguments (hence default).
X::~X() {} // Destructor
X::X(X const& rhs) {}; // Copy constructor
X& operator=(X const& rhs)
{return *this;} // Assignment operator.
// Since C++11 we also define move operations
X::X(X&& rhs) {}; // Move Constructor
X& operator=(X&& rhs)
{return *this;} // Move Assignment
Note:
The default constructor is not built if ANY constructor is defined.
The other methods are not built if the user defines an alternative.
What is slightly more interesting is the default implementation when we have members and a base:
class Y: public X
{
int a; // POD data
int* b; // POD (that also happens to be a pointer)
Z z; // A class
};
// Note: There are two variants of the default constructor.
// Both are used depending on context when the compiler defined version
// of the default constructor is used.
//
// One does `default initialization`
// One does `zero initialization`
// Objects are zero initialized when
// They are 'static storage duration'
// **OR** You use the braces when using the default constructor
Y::Y() // Zero initializer
: X() // Zero initializer
, a(0)
, b(0)
, z() // Zero initializer of Z called.
{}
// Objects are default initialized when
// They are 'automatic storage duration'
// **AND** don't use the braces when using the default constructor
Y::Y()
:X // Not legal syntax trying to portray default initialization of X (base class)
//,a // POD: uninitialized.
//,b // POD: uninitialized.
,z // Not legal syntax trying to portray default initialization of z (member)
{}
//
// Note: It is actually hard to correctly zero initialize a 'automatic storage duration'
// variable (because of the parsing problems it tends to end up a a function
// declaration). Thus in a function context member variables can have indeterminate
// values because of default initialization. Thus it is always good practice to
// to initialize all members of your class during construction (preferably in the
// initialization list).
//
// Note: This was defined this way so that the C++ is backward compatible with C.
// And obeys the rule of don't do more than you need too (because we want the C++
// code to be as fast and efficient as possible.
Y::Y(Y const& rhs)
:X(rhs) // Copy construct the base
,a(rhs.a) // Copy construct each member using the copy constructor.
,b(rhs.b) // NOTE: The order is explicitly defined
,z(rhs.z) // as the order of declaration in the class.
{}
Y& operator=(Y const& rhs)
{
X::operator=(rhs); // Use base copy assignment operator
a = rhs.a; // Use the copy assignment operator on each member.
b = rhs.b; // NOTE: The order is explicitly defined
z = rhs.z; // as the order of declaration in the class.
return(*this);
}
Y::~Y()
{
Your Code first
}
// Not legal code. Trying to show what happens.
: ~z()
, ~b() // Does nothing for pointers.
, ~a() // Does nothing for POD types
, ~X() ; // Base class destructed last.
// Move semantics:
Y::Y(Y&& rhs)
:X(std::move(rhs)) // Move construct the base
,a(std::move(rhs.a)) // Move construct each member using the copy constructor.
,b(std::move(rhs.b)) // NOTE: The order is explicitly defined
,z(std::move(rhs.z)) // as the order of declaration in the class.
{}
Y& operator=(Y&& rhs)
{
X::operator=(std::move(rhs)); // Use base move assignment operator
a = std::move(rhs.a); // Use the move assignment operator on each member.
b = std::move(rhs.b); // NOTE: The order is explicitly defined
z = std::move(rhs.z); // as the order of declaration in the class.
return(*this);
}
Just to expand on Armen Tsirunyan answer here are the signatures for the methods:
// C++03
MyClass(); // Default constructor
MyClass(const MyClass& other); // Copy constructor
MyClass& operator=(const MyClass& other); // Copy assignment operator
~MyClass(); // Destructor
// C++11 adds two more
MyClass(MyClass&& other) noexcept; // Move constructor
MyClass& operator=(MyClass&& other) noexcept; // Move assignment operator
Default methods assigned by compiler for a empty class:
http://cplusplusinterviews.blogspot.sg/2015/04/compiler-default-methods.html
Is that it?
Yes thats it.
Compiler generates by default
A default constructor
A copy constructor
A copy assignment operator
A destructor
for a class
You can see the default constructor, the copy constructor and the assignment operator being generated by default when you use -ast-dump option of Clang
prasoon#prasoon-desktop ~ $ cat empty.cpp && clang++ -cc1 -ast-dump empty.cpp
class empty
{};
int main()
{
empty e;
empty e2 = e;
{
empty e3;
e3 = e;
}
}
typedef char *__builtin_va_list;
class empty {
class empty;
inline empty() throw(); //default c-tor
//copy c-tor
inline empty(empty const &) throw() (CompoundStmt 0xa0b1050 <empty.cpp:1:7>)
//assignment operator
inline empty &operator=(empty const &) throw() (CompoundStmt 0xa0b1590 <empty.cpp:1:7>
(ReturnStmt 0xa0b1578 <col:7>
(UnaryOperator 0xa0b1558 <col:7> 'class empty' prefix '*'
(CXXThisExpr 0xa0b1538 <col:7> 'class empty *' this))))
};