I've had it drilled into my head many many times that if a copy-constructor is provided, an assignment operator must also be provided. However, there are times when a class can use a copy-constructor but not an assignment operator.
For example:
class A {
public:
const int myVar;
A(const int var) : myVar(var) {};
A(const A& other) : myVar(other.myVar) {};
};
So is this a terrible thing to do? Does the assignment operator need to be defined but made private? Is such a class still copy-constructable?
So is this a terrible thing to do?
No, it is not.
Not all classes need to be copy constructible as well as assignable. It is perfectly valid to have copy constructible yet non assignable classes.
Is such a class still copy-constructable?
Yes it is.
As long as your class provides a public copy constructor, Your class is copy constructible.
Does the assignment operator need to be defined but made private?
It depends on your usage.
If your class needs to be assignable then it should ideally not have a const member.
The default compiler generated copy assignment operator will not work if your class has an const member because it tries to assign to a const member which is not allowed. So if your code needs a copy assignment operator you will have to provide your own overloaded version. But, Anyway this overloaded version cannot provide expected assignment semantics.
If your class objects do not need to be Assignable then do not define it. If your code does accidentally uses it the compiler will generate an error anyways.
Related
In C++, if we have this class
class Uncopyable{
public:
Uncopyable(){}
~Uncopyable(){}
private:
Uncopyable(const Uncopyable&);
Uncopyable& operator=(const Uncopyable&);
};
and then we have a derived class
class Dervied: private Uncopyable{
};
My question is: why won't this generate a compile time error when the compiler generates the default copy constructor and assignment operators in the derived class ? Won't the generated code try to access base class private members ?
C++11 12.8/7 states "If the class definition does not explicitly declare a copy constructor, one is declared implicitly." so Dervied has an implicitly declared copy constructor. 12.8/11 says:
An implicitly-declared copy/move constructor is an inline public member of its class. A defaulted copy/move constructor for a class X is defined as deleted (8.4.3) if X has:
a variant member with a non-trivial corresponding constructor and X is a union-like class,
a non-static data member of class type M (or array thereof) that cannot be copied/moved because overload resolution (13.3), as applied to M’s corresponding constructor, results in an ambiguity or a function that is deleted or inaccessible from the defaulted constructor,
a direct or virtual base class B that cannot be copied/moved because overload resolution (13.3), as applied to B’s corresponding constructor, results in an ambiguity or a function that is deleted or inaccessible from the defaulted constructor,
any direct or virtual base class or non-static data member of a type with a destructor that is deleted or inaccessible from the defaulted constructor,
for the copy constructor, a non-static data member of rvalue reference type, or
for the move constructor, a non-static data member or direct or virtual base class with a type that does not have a move constructor and is not trivially copyable.
Specifically, the third bullet applies: Dervied has a direct base class Uncopyable that cannot be copied because overload resolution results in a function that is inaccessible from Dervied::Dervied(const Dervied&). As a result Dervied's implicitly declared copy constructor is declared as deleted, resulting in a compile time error if and only if that copy constructor is called.
why won't this generate a compile time error when the compiler generates the default copy constructor and assignment operators in the derived class ?
Because the compiler generates them only when they are needed by the code being compiled. Write some code using the derived class where the copy constructor and/or assignment operator are involved, and you will see the compile-time error you are looking for.
The private in the inheritance makes them private to Derived, it can still see them, classes that use Derived can't.
The derived class will inherit the private copy constructor but will not need to use it unless you copy an object of derived type, as in this example.
The compiler does not auto-generate constructors/operators unless they are used and no other constructor/operator can be used to do that operation (i.e. a copy operation can be used in some situations where a move operation would suffice). The latter statement results in the following set of rules.
Here are the rules to the auto-generation of certain member functions:
Default constructor (if no other constructor is explicitly declared)
Copy constructor if no move constructor or move assignment operator
is explicitly declared. If a destructor is declared generation of a
copy constructor is deprecated.
Move constructor if no copy
constructor, move assignment operator or destructor is explicitly
declared.
Copy assignment operator if no move constructor or move assignment
operator is explicitly declared. If a destructor is declared
generation of a copy assignment operator is deprecated.
Move assignment operator if no copy constructor, copy assignment operator
or destructor is explicitly declared.
Destructor
The list is taken from this Wikipedia page.
One class cannot call private methods on another class, but it can inherit as much as it is coded too. This code just includes the member functions from Uncopyable in Derived.
Imagine if you wrote a class inheriting from std::vector. You can still erase, insert, push_back and do all those sorts of things. Because these are all public or protected vector member functions, they in turn call implementation specific private functions that do the low level things like manage memory. Your code in this derived class couldn't call those memory management functions directly though. This is used to insure the creators of the vector can change the internal details freely without breaking your use of the class.
If your example is what the code actually looks like, then this it is a common pattern used to make things that cannot be copied. It would make code like the following produce a compiler error:
Derived Foo;
Derived Bar;
Foo = Bar
It would also make the code throw an error on the following:
int GetAnswer(Derived bar)
{ /* Do something with a Derived */ }
Derived Foo;
int answer = GetAnser(Foo);
This example fails because a copy of foo is made and passed as the parameter in the function GetAnswer.
There are many reasons why something might not be copyable. The most common I have experienced is that the object manages some low level resource a single file, an opengl context, an audio output, etc... Imagine if you had a class that managed a log file. If it closed the file in the deconstructor, what happens to the original when a copy is destroyed.
Edit: to pass an Uncopyable class to a function, pass it by reference. The Following function does not make a copy:
int GetAnswer(Derived& bar)
{ /* Do something with a Derived */ }
Derived Foo;
int answer = GetAnser(Foo);
It would also cause a compiler error if all the constructor were private and the class was instantiated. But even if all the member function even constructors were private and the class was never instantiated that would be valid compiling code.
Edit: The reason a class with constructor is that there maybe other way to construct it or it maybe have static member functions, or class functions.
Sometimes factories are used to build object which have no obvious constructor. These might have functions to whatever magic is required to make the umakeable class instance. The most common I have seen is just that there was another constructor that was public, but not in an obvious place. I have also seen factories as friend classes to the unconstructable class so they could call the constructors and I have seen code manually twiddle bits of memory and cast pointers to the memory it to an instance of a class. All of these patterns are used to insure that a class is correctly created beyond just the guarantees C++ supplies.
A more common pattern I have seen is static member functions in classes.
class uncreateable
{
uncreateable() {}
public:
static int GetImportantAnswer();
};
Looking at this it can be seen that not only do I not need to create a instance of the class to call GetImportantAnswer() but I couldn't create an instance if I wanted. I could call this code using the following:
int answer;
answer = uncreateable::GetImportantAnswer();
Edit: Spelling and grammar
Well, actually this program does not compile with g++:
#include <iostream>
using namespace std;
class Uncopyable{
public:
Uncopyable(){}
~Uncopyable(){}
private:
Uncopyable(const Uncopyable&) {cout<<"in parent copy constructor";}
Uncopyable& operator=(const Uncopyable&) { cout << "in parent assignment operator";}
};
class Derived: private Uncopyable{
};
int main() {
Derived a;
Derived b = a;
}
compiler output:
$ g++ 23183322.cpp
23183322.cpp:10:88: warning: control reaches end of non-void function [-Wreturn-type]
Uncopyable& operator=(const Uncopyable&) { cout << "in parent assignment operator";}
^
23183322.cpp:13:7: error: base class 'Uncopyable' has private copy constructor
class Derived: private Uncopyable{
^
23183322.cpp:9:5: note: declared private here
Uncopyable(const Uncopyable&) {cout<<"in parent copy constructor";}
^
23183322.cpp:19:15: note: implicit copy constructor for 'Derived' first required here
Derived b = a;
^
1 warning and 1 error generated.
I have a simple question:
class my
{
};
my ob;
Compiler allows me to create an object which makes sense. And, I am aware that you can't create object where the constructor is private.
To me it looks that, everything inside the class is private but obviously not the default constructor(because it is allowing me to create the object as default constructor should be public). But what confuses me is that there is no public section in the class.
So, does it create a public section only to put a default constructor under it in this case?
Or there is something else going on and my rationale is incorrect?
Also, how are accesses public, private and protected internally organised/tracked when an object is created/accessed?
I got this question as I never created an object of an empty class until now.
If you do not declare any constructor yourself, C++ compilers will always generate a public trivial constructor for you. More than that even, it will also implicitly create a public copy constructor and assignment operator.
From C++11 standard 12.1.5:
If
there is no user-declared constructor for class X, a constructor having no parameters is implicitly declared
as defaulted. An implicitly-declared default constructor is an inline public member of its class.
and 12.8.7, 12.8.11:
If the class definition does not explicitly declare a copy constructor, one is declared implicitly. [...] An implicitly-declared copy [...] constructor is an inline public member of its class.
and finally 12.8.18, 12.8.20, 12.8.22:
If the class definition does not explicitly declare a copy assignment operator, one is declared implicitly. [...] If the definition of a class X does not explicitly declare a move assignment operator, one will be implicitly
declared [...]. An implicitly-declared
copy/move assignment operator is an inline public member of its class.
Note that a move assignment operator will only be generated under certain circumstances, which are beyond the scope of this question, see 12.8.20 for more details.
If you want a private constructor you have to declare it yourself:
class my { my() {} };
If you want to prevent the generation of copy constructor or assignment operator you can either declare, but not implement them:
class my { my(my const &); };
Or, since C++11, explicitly delete them:
class my { my(my const &) = delete; };
Yes, the compiler will produce the default constructor and the default copy constructor and default assignment operators as "public" - because anything else would make the class rather useless...
Of course, those constructors would be rather simple - in fact, it can be replaced with "nothing", since constructing an empty class will do nothing.
The compiler generated default constructor (and other operators) are automatically public. If you want the default constructor to be private then you need to specify this yourself my declaring it within a private section of your class.
The concepts of private, protected and public are only relevant to the compiler. They have no meaning and are not tracked at runtime.
The compiler will generate the default constructor as inline public if it is not defined by the user, the relevant section of the C++ draft standard is 12.1/5:
If there is no user-declared constructor for class X, a constructor having no parameters is implicitly declared as defaulted (8.4). An implicitly-declared default constructor is an inline public member of its class.
Usually compiler by default generates 4 things at the time of creation of object.
Default constructor
Copy constructor
Copy assignment operator
Destructor
For example:
class First {
First(){} //default constructor
First(const First &){} //copy constructor
First& operator=(const First&){ //Copy assignment operator
return *this;
}
~First(){} //Destructor
}
These are implicitly inline public member, unless there is no user declared constructor.
//------------------------------------------------------------------------------
struct A
{
A(){}
A(A&&){}
A& operator=(A&&){return *this;}
void operator()(){}
private:
A(const A&);
A& operator=(const A&);
int x;
};
//------------------------------------------------------------------------------
int main()
{
A a;
std::function<void()> func(std::move(a));
}
'A::A' : cannot access private member declared in class 'A'
It seems like when I capture something by reference or const I can make a non-copyable lambda. However when I do that it actually works to give it to a std::function.
The short answer is that the C++11 specification requires your A to be CopyConstructible to be used with std::function.
The long answer is this requirement exists because std::function erases the type of your functor within the constructor. To do this, std::function must access certain members of your functor via virtual functions. These include the call operator, the copy constructor and the destructor. And since these are accessed via a virtual call, they are "used" whether or not you actually use std::function's copy constructor, destructor or call operator.
std::function requires functors to be copyable.
This is an arbitrary design decision. Possible alternatives could be:
Non-copyable functors are allowed, but attempting to copy the resulting std::function causes a crash/exception.
Non-copyable functors are allowed, and std::functions themselves are always non-copyable.
...
It's impossible to only require copyability if the resulting std::function is copied, because it can be copied in a different TU (translation unit).
So, if you ask me, the current behavior is the lesser evil.
It's a bug in Visual Studio. It attempts to ellide a copy (when actually it should be trying to ellide a move), which requires an accessible copy constructor. The best solution is simply to declare the copy constructor/assignment operator and never define them. The class will still be non-copyable but the code will compile, because VS will never attempt to actually call the copy constructor.
I'm confused... why isn't my assignment operator getting called here?
template<typename This>
struct mybase
{
This& operator =(const This &other)
{
__debugbreak(); // The debugger should break here, but doesn't.
return static_cast<This &>(*this):
}
};
struct myderived : mybase<myderived>
{
int x;
};
int main()
{
myderived a = myderived(); // And yes, I know it's redundant...
myderived b = myderived();
a = b;
return 0;
}
Because the default assignment operator in derived will hide the overloaded in base.
mybase::operator= is hidden by the automatically generated copy assignment operator myderived::operator=.
You can use a using declaration to make the base class operator visible in the derived class.
EDIT: added example per request:
template<typename This>
struct mybase
{
This& operator =(const This &other)
{
//__debugbreak(); // The debugger should break here, but doesn't.
return static_cast<This &>(*this);
}
};
struct myderived : mybase<myderived>
{
using mybase<myderived>::operator=;
int x;
};
int main()
{
myderived a = myderived(); // And yes, I know it's redundant...
myderived b = myderived();
a = b;
}
This compiles fine with Visual C++ 10.0 and with Comeau Online. The latter means, in practice, that it's good standard C++. However, the code does not compile with MinGW g++ 4.4.1 (compiler bug).
EDIT 2: Actually, checking now, with Visual C++ 10.0 it compiles but the base class operator is not invoked. So maybe g++ is correct. using is generally the way to bring in a base class assignment operator (or whatever), but in this case it has the same signature as the derived class’ copy assignment operator, and I do not yet know whether Visual C++ behavior is correct or not – it is a corner case of the language.
EDIT 3: I checked N3290 (the standard draft that is identical to C++11), and it says
§12.8/18:
If the class definition does not explicitly declare a copy assignment operator, one is declared implicitly. If the class definition declares a move constructor or move assignment operator, the implicitly declared copy assignment operator is defined as deleted; otherwise, it is defined as defaulted (8.4).
I personally interpret that as saying that with the using declaration in place the class “declares” a copy assignment operator, and that one should therefore not be implicitly generated (as it seems that Visual C++ 10.0 does). However, this is a corner case of the language. Others may possibly interpret this differently, and as noted above, compilers differ!
Cheers & hth.,
This line:
a = b;
obviously requires that myderived have overloaded the copy assignment operator. It can be implicitly generated by the compiler, or defined by the myderived class explicitly:
12.8 Copying class objects [class.copy]
9. A user-declared copy assignment operator X::operator= is a
non-static non-template member function of class X with exactly one
parameter of type X, X&, const X&, volatile X& or const
volatile X&.
You have attempted to create a user-declared copy assignment operator in your mybase class, but it's not actually a copy assignment operator according to the C++ standard. Imagine if we did a type substitution for This with myderived:
// Hypothetical class generated by the compiler from
// the mybase template class with This = myderived
struct mybase_myderived
{
myderived& operator =(const myderived &other)
{
// ...
}
};
Clearly that's not a copy assignment operator, because the parameter other is of type const myderived&, not const mybase&. Had the other parameter be of type const mybase&, or mybase, or mybase&, then it would be a valid copy assignment operator, which can be called by the default copy assignment operator in myderived. But it isn't in this case, so the compiler still generates a default copy assignment operator for mybase, which of course does nothing in this case.
The compiler-generated default copy assignment operator in myderived calls the compiler-generated default copy assignment operator in mybase. So what ends up happening, as As a result, the operator=(const myderived &other) overload is never called.
The reason why the compiler doesn't just call mybase::operator= directly is because it's been hidden by the compiler-generated copy assignment operator in myderived, as Alf P. Steinbach points out in this answer.
Because the compiler introduces the default assignment operator in myderived. Override it and call your base assignment operator yourself. Or perhaps a using directive will help? Try using mybase::operator= in myderived body.
While learning the concept of "copying members", the book gives the following statement.
In addition, a default assignment cannot be generated if a nonstatic member is a reference, a const,or a user-defined type without a copy assignment.
I do not quite understand what does this statement really want to deliver? Or which kind of scenario does this statement refer to? Thanks.
This statement has to do with the compiler automatically generating the default assignment operator function for a class you write (i.e. user-defined type). The default assignment works by copying all the members over to a new instance. This statement covers three cases where a default assignment would not be able to be generated:
1) When a member is a reference (i.e. refers to an instance of a variable, like a pointer)
class Foop {
int& reference;
};
2) When a member variable is constant
class Foople {
const int someConst;
};
3) When some other class does not have a copy-constructor and you have a member variable of that type, obviously it cannot be copied using the default method (which uses copy-constructors)
class Uncopyable {
private:
Uncopyable(Uncopyable const& other);
};
class Fleep {
Uncopyable uncopyable;
};
In these cases, you would need to write your own assignment operator (or possibly do without).
If you have a member in your class which is not static (shared between all instances of class), and is either
a reference (high level pointer)
a constant
a user-defined type with dynamic data (the same as the class we're talking about)
The default = operator and copy constructor is no longer valid and you should write manual versions of those.
class ClassA
{
int& _myReferenceMember;
const int _myConstant;
ClassB _objWhereClassBHasNoCopyConstructor;
}
Above are examples of the three cases you described. And as you quoted, you must write a custom copy constructor (if you want a copy constructor at all) in such a case, or change your member variables.
It refers to the distinction between:
class A { int a; };
and
class B { int& a; };
For class A, the compiler will generate an implicit assignment operator (=), but in the case of B, it cannot. This is because references in C++ don't have pointer semantics. i.e. you cannot change what a reference point to after it is constructed, hence, the implicit copy constructor would not be able to copy that member. The same thing goes for const members (which are explicitly marked as being immutable) and members which don't have a assignment operators (implicit or explicit).
The default assignment operator for A would essentially do this:
class A
{
A& operator=(A const& a_) { a = a_.a; }
int a;
};