direct initialization and copy initialization

Hey, guys, I have a few questions regarding direct initialization and copy initialization.
1. if I defined a variable and then defined another variable using the first variable, is this direct initialization or copy initialization?
1
2
string a(10,'a');
string b(a);


so in the case above, b is initialized with the constructor of string class or the copy constructor of string class?

2. I read on my textbook that copy initialization would happen when we return an object from a function that has a nonreference return type. I'm wondering what exactly is the underlying mechanism behind this? cuz in order to verify this claim, I tried it with an user-defined class which is shown below
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
#include <iostream>
#include <vector>
#include <memory>
using namespace std;
class Hasptr
{
public:
    Hasptr(const string &s = string()): ps(new string(s)), i(0) {cout << 0 << endl;}
    Hasptr(const Hasptr& ori){*ps = *ori.ps; i = ori.i; cout << 1 << endl;}
    void print(){cout << *ps << " " << i << endl;}
    private:
    string *ps = new string();
    int i;
};
Hasptr func(Hasptr &a) { Hasptr b = a;  return b;}
int main()
{
    Hasptr x("fs");
    Hasptr y = func(x);
}

Basically, there is a class named Hasptr with a constructor which would print '0' every time it's called and another copy constructor which would print '1' every time being called, so in the main function, I defined an object named x with type Hasptr, and called a function named func to initialize another object y with type Hasptr.
and the output of this program is
1
2
0
1

while I actually expect the output to be
1
2
3
4
0
1
1
1

The 0 is because I first directly initialized the object x. The first 1 is because I called the function func to initialize y and func used copy initialization once in its function body. The second 1 is because I returned a object b from function func and this function had nonreference return type, so according to my textbook, a copy initialization should happen here once. And the last 1 is because the object y is also copy initialized.

These 2 questions are all I can think of for now. I hope that you guys can help me out here. I'd really appreciate it!
Last edited on
I don't think there is any difference. The copy constructor is defaulted on strongly typed objects (explicit constructors), so I would assume that the copy constructor simply invokes memcpy on it's POD members (plain old data) and the copy constructors of it's object memers.

This is just an educated guess, though.

You could always search the reference for your answer, though: www.cppreference.com
to IWishIKnew:
Thanks for replying. As a matter of fact, I have it on my textbook that in the code
1
2
string a(10,'a');
string b(a);

variable b here is directly initialized, but the thing is that I tried a similar initialization with my own code which is shown below, it showed that this format of initialization should be copy initialization.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
#include <iostream>
#include <vector>
#include <memory>
using namespace std;
class Hasptr
{
public:
    Hasptr(const string &s = string()): ps(new string(s)), i(0) {cout << 0 << endl;}
    Hasptr(const Hasptr& ori){*ps = *ori.ps; i = ori.i; cout << 1 << endl;}
    void print(){cout << *ps << " " << i << endl;}
    private:
    string *ps = new string();
    int i;
};
int func(int a) { int b =3;  return b;}
int main()
{
    Hasptr a("fs");
    Hasptr b(a);
}

The output is
1
2
0
1

which suggests that direct initialization happens first with a and then copy initialization happends with b.
string a(10,'a');
string b(a);
so in the case above, b is initialized with the constructor of string class or the copy constructor of string class?


This is copy-initialization, and the copy constructor is selected.

and the output of this program is
0
1


You encountered copy elision, the rule that lets copy constructors be skipped in several situations: http://en.cppreference.com/w/cpp/language/copy_elision

Semantically, you requested the following constructors to be called:
Hasptr x("fs");: constructor from string
Hasptr b = a;: copy constructor
return b;: copy constructor (would be move if available)
Hasptr y = func(x);: copy constructor

but the compiler is allowed to unify main()'s object "y" and func()'s object "b", so when func() executes "Hasptr b = a", it is actually constructing main()'s object "y" (by calling the copy constructor with "a" as the argument)
Last edited on
To Cubbi:
Thanks for replying. I have one more question in mind after reading your reply. What exactly happens when a non-reference object is returned by a function? Why the copy constructor is needed for this?
I can understand that the copy constructor is called when a non-reference object is passed as an argument to a function, for example
1
2
3
4
5
6
void func(int x){ }
int main()
{
    int a = 0;
    func(a);
}

here in func, an int type object "x" is copy initialized from "a" when func is called, and thus copy constructor is called as well, but why would copy constructor be called when we return an object? It seems to me that no new object is initialized not to mention being copy initialized.
The function call expression creates a nameless object, initialized with the return statement's argument.

Consider std::string s = "abcdef"; std::cout << s.substr(1,2); the two-character string "bc" that is being printed is a new object, different from s.
To Cubbi,
Thanks for the clarification, but after giving some thoughts about your reply, I still have some doubts. If the program actually creates a nameless object to store the returned object, then theoretically I should be able to use a function call as a left value, but I tried it with the code below and apparently it generated an error implying that's not the case:
1
2
3
4
5
6
7
#include <iostream>
using namespace std;
int func() {int a = 0; return a;}
int main()
{
    func() = 1;
}

No, because an unnamed temporary value isn't an lvalue.

Cubbi is telling you the truth, as any C++ textbook will confirm.
It may help to note that you can call member functions on that temporary object, e.g. std::cout << s.substr(1,2).size(). Within the code of size(), the this pointer points to the object that the function substr() returned.
Yes, it indeed should be an object if I see it from this angle. So what you mean is that a nameless object would be created to store the returned object, but this nameless object itself is not a lvalue?
lvalue/rvalue are categories of expressions, they do not apply to objects.

a function call expression, if the function return type is not a reference type, is an rvalue expression.

there's a summary at http://en.cppreference.com/w/cpp/language/value_category
Last edited on
Get it. Thanks a lot!
Topic archived. No new replies allowed.