Understanding the process of initialization - c++

This is my attempt to understand how class initialization works. I'm not sure about everything, and that is why I'm asking this question. This is what I believe happens when we do the following:
T t = u;
Constructs an object of type T from u. This then becomes:
T t = T(u);
Calls the copy-constructor:
T t( T(u) );
Okay the second is the part I'm not understanding. I read somewhere that T t = u is made into T t(T(u)). But if that is true why doesn't this print "copy-constructor":
struct T
{
template <class U>
T(U) {
std::cout << "constructs an object of type T...\n";
}
T(T const &)
{
std::cout << "copy-constructor";
}
T& operator=(T const &)
{
std::cout << "assignment operator"; return *this;
}
T() = default;
};
int main()
{
T t(T(5));
}
Actually, all this does is print "constructs an object of type T". Why isn't the copy-constructor called here? T(5) can be made into an object of type T const & which is passed into the constructor of T so shouldn't the appropriate constructor be called.
I'd really like some insight to this. I've been trying to understand this for a while.

There's a special rule for this situation. The compiler is allowed to skip the copy constructor, even though it has side effects, provided the expression with the copy constructor would have been legal. So
T t = u;
is, as you say, equivalent to
T t(T(u));
The compiler skips the copy constructor and treats this as
T t(u);
One situation where this would not be allowed would be a class with a private copy constructor. In that case, T t(T(u)); would not be legal, and the compiler would not be allowed to make it legal by skipping the copy constructor.

Related

Error spotted in C++ Primer 5th Edition (copy intialization vs direct initialization)

Hi i am trying to understand how copy constructor works and looking at an example. The example is as follows:
{//new scope
Sales_data *p = new Sales_data;
auto p2 = make_shared<Saled_data>();
Sales_data item(*p); // copy constructor copies *p into item
vector<Sales_data> vec;
vec.push_back(*p2);// copies the object to which p2 points
delete p;
}
My question is :
Why it is written that "copy constructor copies *p into item"? I mean, item is direct initialized. If we would have written Sales_data item = *p; then it will be called copy initialized, so why they have written copy constructor copies *p into item in the comment.
Now, to verify this for myself, i tried creating a simple example myself, but there also i am unable to understand the concept properly. My custom example is as follows:
#include<iostream>
#include<string>
class MAINCLASS{
private:
std::string name;
int age =0;
public:
MAINCLASS(){
std::cout<<"This is default initialization"<<std::endl;
}
MAINCLASS(MAINCLASS &obj){
std::cout<<"This is direct initialization"<<std::endl;
}
MAINCLASS(const MAINCLASS &obj):name(obj.name),age(obj.age){
std::cout<<"This is copy initialization"<<std::endl;
}
};
int main(){
MAINCLASS objectone;
MAINCLASS objecttwo =objectone;
MAINCLASS objectthree(objectone);
return 0;
}
Now when i run this program, i get the following output:
This is defalut initialization
This is direct initialization
This is direct initialization
My question from this program is as follws:
Why are we not getting the output "this is copy initialization" in the second case when i write MAINCLASS objecttwo =objectone;? I have read that in direct initialization function matching is used and in copy constructor , we copy the right hand operand members into left hand operand members. So when i write MAINCLASS objecttwo =objectone; it should call the copy constructor and print "this is copy initialization" on the screen. But instead it is direct initializing the object. What is happening here?
Despite the poor choice of name, copy initialization is orthogonal to copy constructors.
A copy constructor is any constructor whose first parameter is a lvalue reference to its class type, and can be called with just one argument. It's just a constructor that can initialize new objects from existing objects. That's pretty much all there is to it. Both the constructors you declared are in fact copy constructors. This one would be too
MAINCLASS(MAINCLASS volatile &obj, void *cookie = nullptr) {
// .. Do something
// This is a copy c'tor since this is valid:
// MAINCLASS volatile vo;
// MAINCLASS copy1_vo(vo);
}
And as the other answers noted copy initialization is simply the name for a family of initialization contexts. It includes initialization involving =, passing arguments to functions, return statements and throw expressions (and I'm probably forgetting something). Direct initialization involves other contexts.
A copy constructor can be used in any of the above. Be it copy initialization or direct initialization. The difference between the two - as appertains to constructors - is how an overload set of constructors is built. Copy initialization doesn't make use of constructors declared explicit. For instance, in this example
struct Example {
Example() = default;
explicit Example(Example const&) {}
};
int main() {
Example e;
Example e1(e); // Okay, direct initialization
Example e2 = e1; // Error! Copy initialization doesn't make use of explicit constructor
}
Even though we have a copy constructor, it can't be called in a copy-initialization context!
As far as the unexpected print out of your program, it's simply a matter of overload resolution choosing a more matching function. Your origin object is not declared const. So binding it to a non-const lvalue reference is simply the preferred choice in overload resolution.
Don't confuse copy construction and copy initialisation. You can copy-construct using direct or copy initialisation.
Copy initialisation refers to a set of initialisation syntax and semantics. This includes the T a = b syntax.
The copy constructor is a special class method that takes an argument of said class. This method should only take one parameter (both T& or const T& will do). Copy construction occurs when that function is called.
With this in mind, we can go on to answer your questions.
Why it is written that "copy constructor copies *p into item"? I mean, item is direct initialized. If we would have written Sales_data item = *p; then it will be called copy initialized...
Both Sales_data item = *p and Sales_data item(*p) call the copy constructor. But, the former uses copy initialisation (T a = b), whereas the latter uses direct initialisation (T a(b)).
Why are we not getting the output "this is copy initialization" in the second case when i write MAINCLASS objecttwo =objectone;?
Actually, the issue here isn't whether it's copy/direct initialised. This is an issue of lvalue/rvalue overload resolution.
Consider the following program:
#include <iostream>
void f(int& i) { std::cout << "int&\n"; }
void f(const int& i) { std::cout << "const int&\n"; }
int main() {
f(1); // f(const int&)
int i = 2;
f(i); // f(int&)
}
f is chosen based on whether the value passed is lvalue or rvalue. In the first case, 1 is an rvalue, so f(const int&) is called (see this). In the second case, i is an lvalue, and f(int&) is chosen since it's more general.
So in your case, both MAINCLASS objecttwo =objectone; and MAINCLASS objectthree(objectone); call the copy constructor. And again, the former uses copy initialisation, whereas the latter uses direct initialisation. It's just that both of these calls choose the non-const ref overload instead: MAINCLASS(MAINCLASS&).
Copy initialization and direct initialization is based on the syntax used to construct.
See Confusion in copy initialization and direct initialization.
Which constructor gets invoked is based on overload resolution (and not the syntax to construct)
The compiler invokes the function which best matches the passed arguments to the defined parameters.
In your example since objectone is non-const, the best match is the copy constructor with a non-const parameter. Since the other copy constructor has a const& parameter, it will get invoked for a const object.
Rewriting your example:
#include<iostream>
#include<string>
class MAINCLASS {
private:
std::string name;
int age = 0;
public:
MAINCLASS() {
std::cout << "This is default initialization" << std::endl;
}
MAINCLASS(MAINCLASS& obj) {
std::cout << "This is copy constructor with non-const reference parameter" << std::endl;
}
MAINCLASS(const MAINCLASS& obj) :name(obj.name), age(obj.age) {
std::cout << "This is copy constructor with const reference parameter" << std::endl;
}
};
int main() {
MAINCLASS objectone;
const MAINCLASS const_objectone;
MAINCLASS objecttwo = objectone; // copy initialization of non-const object
MAINCLASS objectthree(objectone); // direct initialization of non-const object
MAINCLASS objectfour = const_objectone; // copy initialization of const object
MAINCLASS objectfive(const_objectone); // direct initialization of const object
return 0;
}
The output would be:
This is default initialization
This is default initialization
This is copy constructor with non-const reference parameter
This is copy constructor with non-const reference parameter
This is copy constructor with const reference parameter
This is copy constructor with const reference parameter

C++14 Function returns array of objects although copy constructor is deleted

from the old C++98 i am aware, that return types a copied (by value) if not mentioned in the function declaration/definition otherwise with the address operator '&'.
Now i am playing around with the concepts of auto and decltype to let the compiler determin the return type. In an example i worte a class A where with exception of the default ctor any other ctors are deleted (class A is taken from a real project - and i investigate some issues). An object of the class A is contructed together with an etl::array (Embedded template library, Array created on the stack with fixed size), see example code below.
#include <etl/array.h>
#include <iostream>
class A {
public:
A(std::uint8_t val) : _val(val){}
A(A const&) = delete; // copy delete
A& operator=(A&) = delete; // copy assign delete
A(A&&) = default; // move default
A& operator=(A&&) = delete;// move assign delete
~A() = default;
void whoAmI(){std::cout << " I am A! " << std::endl;}
private:
std::uint8_t _val;
};
decltype(auto) X(std::uint8_t val) {
return etl::array<A,1>{A{val}};
}
int main()
{
decltype(auto) x = X(5U);
for (auto& it : x) {
it.whoAmI();
}
}
I would expect, that the etl::array will be copied in the main() routine and assigned to the local variable x. I would not expect to have a copy of A in the array, due to the deleted copy ctor. However, the code compiles and i am able to call the function on the element of the etl::array. I cannot understand why this is working and why it is compiling at all? And I wonder what type decltype(auto) finally is. I have chosen decltype(auto) because of Scott-Meyers Item 2 and Item 3. To item 3, i am not sure to have a complete understanding on the decltype topic..
When I step through the code it works fine, leaving me pussled behind..
Any help on this topic is highly appreciated!
Thank you very much for your help, it is enlightening to me.
Now i finally know why it's working :-D - you made my day!
decltype(auto) is used to determine the type and the value category of an expression. When used as a return type, it lets the function decide what the returned value type should be, based on the expression in the return statement. In this example, since you are using a temporary in the returned expression, it will deduce to an rvalue.
In this declaration:
decltype(auto) x = X(5U);
the syntax is copy-initialization, which has the effect of initializing the variable x from the expression X(5U). You have a defaulted move-constructor, and the compiler uses this constructor to initialize x from the rvalue returned from X.
Note that from C++17, due to mandatory copy-elision, you could even delete the move constructor, and the code is still valid, since there is no constructor needed to initialize x.

return const value prevent move semantics

I am a beginner in cpp so excuse me for this question.
I was reading that returning const val prevents move semantics.
therefore I dont understand why the following code is compiled and works normally. is it because only temporary object is being created? in what cases the move semantics cannot being done? thank you in advance!
#include <iostream>
using namespace std;
const string foo()
{
return string("hello");
}
int main()
{
string other = std::move(foo());
}
std::move is just a unconditional cast to rvalue. In your case the return value of std::move(foo()) was const std::string&&. And because move constructor does not take const argument, copy constructor was called instead.
struct C {
C() { std::cout << "constructor" << std::endl; }
C(const C& other) { std::cout << "copy constructor" << std::endl; }
C(C&& other) { std::cout << "move constructor" << std::endl; }
};
const C get() {
return C();
}
int main() {
C c(std::move(get()));
return 0;
}
I was reading that returning const val prevents move semantics. therefore I dont understand why the following code is compiled and works normally.
When move semantics are prevented by some mechanism, this doesn't necessarily mean that the code doesn't compile. Often, it compiles happily, but an expected move construction turns out to be a copy instead.
Example: a type has a user provided copy ctor, which disables compiler-generated move ctors. When we think we move-construct, we don't.
struct Test {
Test() = default;
Test(const Test&) {}
};
Test t1;
Test t2{std::move(t1)}; // Copies!
in what cases the move semantics cannot being done?
Coming to your example, something that is const-qualified can't be used to move-construct another object in any meaningful way. Move construction makes sense when resources can be easily transferred, but const-ness prevents that. Example: a type has compiler-generate move and copy constructors, but we can't move-construct from a const instance.
struct Test {
Test() = default;
};
const Test t1;
Test t2{std::move(t1)}; // Copies!
Besides, it doesn't make sense to move something that is returned by a function by value:
string other = std::move(foo());
When foo() returns by value, you can move-construct from it, unless the return type is const. Hence, to enable move-construction of other:
std::string foo();
string other = foo();
std::move doesn't actually move anything. It is just an "rvalue cast". You cast something to rvalue, and the move constructor / move assignment operator does the actual moving if possible. "If possible" part is the key. In your example the return value is already an rvalue, so std::move literally does nothing. You may even get warnings like "nothing is moved". That is because the move constructor of std::string takes an argument of type std::string&& not const std::string&&. Because of that, the copy constructor is called.

Copy ctor is called instead of move ctor - Can compiler give a warning?

In the following code I want to move-construct an object that has no move-constructor available:
class SomeClass{
public:
SomeClass() = default;
SomeClass(const SomeClass&) = default;
SomeClass( SomeClass&&) = delete;
};
SomeClass& getObject(){
return some_obj;
};
//...
SomeClass obj = std::move( getObject());
The compiler gives an error: "use of deleted function". This is all good.
On the other hand, if it has a move-constructor but getObject() returns a const object, then the copy constructor will be called instead, even though I'm trying to move it with std::move.
Is it possible to make the compiler give a warning / error that std::move won't have any effect since the object can't be moved?
class SomeClass{
public:
SomeClass() = default;
SomeClass(const SomeClass&) = default;
SomeClass( SomeClass&&) = default;
};
const SomeClass& getObject(){
return some_obj;
};
//...
SomeClass obj = std::move( getObject());
If your concern is just knowing for sure that you're getting the best possible performance, most of the time you should probably just trust the compiler. In fact, you should almost never need to use std::move() at all. In your example above, for example, it's not having an effect. Modern compilers can work out when a move should happen.
If you have classes that should always be moved and never copied, then delete their copy constructors.
But maybe you're writing a template function that is going to have terrible performance if you pass it a class without a move constructor, or you're in some other situation I haven't thought of. In that case, std::is_move_constructible is what you probably want. Try this:
#include <type_traits>
#include <boost/serialization/static_warning.hpp>
template<class T>
T &&move_or_warn(T &t)
{
BOOST_STATIC_WARNING(std::is_move_constructible<T>::value);
return std::move(t);
}
Now if you do SomeClass obj = std::move_or_warn( getObject());, you should get a compiler warning if the object can't be moved. (Although I'd probably use the normal std::move and call std::is_move_constructible seperately.)
Unfortunately, C++ doesn't (yet) have a standard way to produce the sort of programmer-specified warning you're looking for, which is why I had to use boost. Take a look here for more discussion about generating warnings.
The problem comes from const rvalue. Such arguments match const T& better than T&&. If you really want to ban such arguments, you can add an overloaded move constructor:
SomeClass(const SomeClass&&) = delete;
Note: What you are trying is to ban such arguments, rather to ban the move behavior. Because we are usually not able to "steal" resource from a const object even if it is an rvalue, it is reasonable to call copy constructor instead of move constructor. If this is an XY problem, you should consider if it is really intended to ban such arguments.
Is it possible to make the compiler give a warning / error that std::move won't have any effect since the object can't be moved?
It is not exactly true that std::move won't have any effect. The following code (try it on wandbox):
void foo(const SomeClass&) {
std::cout << "calling foo(const SomeClass&)" << std::endl;
}
void foo(const SomeClass&&) {
std::cout << "calling foo(const SomeClass&&)" << std::endl;
}
int main() {
foo(getObject());
foo(std::move(getObject()));
}
will output
calling foo(const SomeClass&)
calling foo(const SomeClass&&)
even though your object has a deleted move constructor.
The reason is that std::move doesn't by itself "move" anything. It just does a simple cast (C++17 N4659 draft, 23.2.5 Forward/move helpers):
template <class T> constexpr remove_reference_t<T>&& move(T&& t) noexcept;
Returns: static_cast<remove_reference_t<T>&&>(t)
This is why the compiled doesn't give a warning - everything is perfectly legal, the cast you are doing has nothing to do with the deleted move constructor, and overload resolution selects the copy constructor as the best match.
Of course, you can define your own move with different semantics than std::move if you really need such semantics (like the one in matthewscottgordon's answer).

Can we return objects having a deleted/private copy/move constructor by value from a function?

In C++03 it is impossible to return an object of a class having a private non-defined copy constructor by value:
struct A { A(int x) { ... } private: A(A const&); };
A f() {
return A(10); // error!
return 10; // error too!
}
I was wondering, was this restriction lifted in C++11, making it possible to write functions having a class type return type for classes without constructors used for copy or move? I remember it could be useful to allow callers of a function use the newly returned object, but that they are not able to copy the value and store it somewhere.
Here is how it can work
A f() {
return { 10 };
}
This works even though A has no working copy or move constructor and no other constructor that could copy or move an A!
To make use of this feature of C++11, the constructor (taking int in this case) has to be non-explicit though.
The restriction has not been lifted. As per the access specifier, there is a note in §12.8/32 that explains:
two-stage overload resolution must be performed regardless of whether copy elision will occur. It determines the constructor to be called if elision is not performed, and the selected constructor must be accessible even if the call is elided.
As of the deleted copy/move constructors §8.4.3/2 states that
A program that refers to a deleted function implicitly or explicitly, other than to declare it, is ill-formed. [ Note: This includes calling the function implicitly or explicitly and forming a pointer or pointer-to-member to the function. It applies even for references in expressions that are not potentially-evaluated. If a function is overloaded, it is referenced only if the function is selected by overload resolution. — end note ]
Not sure about this particular case, but my understanding of the quote is that, if after the overload resolution in §12.8/32 the deleted copy/move constructor is selected, even if the operation is elided, that could constitute a reference to the function, and the program would be ill formed.
The above code is still ill-formed in C++11. But you could add a public move constructor to A and then it would be legal:
struct A
{
A(int x) {}
A(A&&);
private:
A(A const&);
};
A f() {
return A(10); // Ok!
}
I was wondering, was this restriction lifted in C++11?
How could it be? By returning something by value, you are by definition copying (or moving) it. And while C++ can allow that copy/move to be elided in certain circumstances, it's still copying (or moving) by the specification.
I remember it could be useful to allow callers of a function use the returned object, but that they are not able to copy the value and store it somewhere.
Yes. You get rid of the copy constructor/assignment, but allow the value to be moved. std::unique_ptr does this.
You can return a unique_ptr by value. But in doing so, you are returning an "prvalue": a temporary that is being destroyed. Therefore, if you have a function g as such:
std::unique_ptr<SomeType> g() {...}
You can do this:
std::unique_ptr<SomeType> value = g();
But not this:
std::unique_ptr<SomeType> value1 = g();
std::unique_ptr<SomeType> value2 = g();
value1 = value 2;
But this is possible:
std::unique_ptr<SomeType> value = g();
value = g();
The second line invokes the move assignment operator on value. It will delete the old pointer and move the new pointer into it, leaving the old value empty.
In this way, you can ensure that the contents of any unique_ptr is only ever stored in one place. You can't stop them from referencing it in multiple places (via pointers to unique_ptr or whatever), but there will be at most one location in memory where the actual pointer is stored.
Removing both the copy and move constructors creates an immobile object. Where it is created is where it's values stay, forever. Movement allows you to have unique ownership, but without being immobile.
You could probably hack together a proxy to do the trick if you really wanted, and have a converting constructor that copies the value stored within the proxy.
Something along the lines of:
template<typename T>
struct ReturnProxy {
//This could be made private, provided appropriate frienship is granted
ReturnProxy(T* p_) : p(p_) { }
ReturnProxy(ReturnProxy&&) = default;
private:
//don't want these Proxies sticking around...
ReturnProxy(const ReturnProxy&) = delete;
void operator =(const ReturnProxy&) = delete;
void operator =(ReturnProxy&&) = delete;
struct SUPER_FRIENDS { typedef T GO; };
friend struct SUPER_FRIENDS::GO;
unique_ptr<T> p;
};
struct Object {
Object() : data(0) { }
//Pseudo-copy constructor
Object(ReturnProxy<Object>&& proxy)
: data(proxy.p ? proxy.p->data : throw "Don't get sneaky with me \\glare")
{
//steals `proxy.p` so that there isn't a second copy of this object floating around
//shouldn't be necessary, but some men just want to watch the world burn.
unique_ptr<Object> thief(std::move(proxy.p));
}
private:
int data;
Object(const Object&) = delete;
void operator =(const Object&) = delete;
};
ReturnProxy<Object> func() {
return ReturnProxy(new Object);
}
int main() {
Object o(func());
}
You could probably do the same in 03, though, using auto_ptrs. And it obviously doesn't prevent storage of the resultant Object, although it does limit you to one copy per instance.