Call Constructor from Copy Constructor -> Stack Overflow

Pages: 123
> Why is the object constructed AGAIN here:

Repeat: a temporary object would be constructed.
The anonymous temporary is a different object; an object of type GString is required for the rhs of the assignment operator; there is an implicit conversion from const char* to GString via the converting constructor.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
#include <iostream>
#include <string>
#include <iomanip>

struct gstring
{
    gstring() : name("") { show( "default constructed" ) ; }
    gstring( const char* name ) : name(name) { show( "constructed" ) ; }
    ~gstring() { show("destroyed") ; }

    gstring& operator= ( const gstring& that ) 
    { 
        v = that.v ; 
        show( "assigned to " ) ; 
        std::cout << "    note: rhs of assignment is gstring at address " << std::addressof(that) << '\n' ;
        return *this ; 
    }

    const std::string name ;
    int v = 0 ;

    void show( const char* what ) const
    {
        static int cnt = 0 ;
        std::cout << "step #" << ++cnt << ". gstring " << std::quoted(name)
                  << " at address " << this << ' ' << what << '\n' ;
    }
};

int main()
{
    static gstring str( "a string" ) ;

    {
        std::cout << "\n*** assign const char*\n" ;
        str = "anonymous temporary" ;
    }
    std::cout << "*** after assign (temporary object destroyed)\n\n" ;
}

step #1. gstring "a string" at address 0x602e00 constructed

*** assign const char*
step #2. gstring "anonymous temporary" at address 0x7fff6bb7ef58 constructed
step #3. gstring "a string" at address 0x602e00 assigned to 
    note: rhs of assignment is gstring at address 0x7fff6bb7ef58
step #4. gstring "anonymous temporary" at address 0x7fff6bb7ef58 destroyed
*** after assign (temporary object destroyed)

step #5. gstring "a string" at address 0x602e00 destroyed

http://coliru.stacked-crooked.com/a/a09027a1f5827333
Thanks, but after a couple of tests my program keeps crashing as Heap corruption detected

1
2
3
GString my = "Hello,I,Am,Micheael";

cout << my.SubString(0,5);


This crashes. Here is what the program does:

1) Constructs the my GString

2) Calls the SubString()method on my

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
GString GString::SubString(const int beginIndex, const int endIndex) const
{	
	// IF NOT-IN-RANGE, return empty string
	if (!(beginIndex >= 0 && beginIndex < size    &&    endIndex >= 0 && endIndex < size))
		return GString();

	// The last "+1" is for the NULL character (\0) 
	int totalSize = (endIndex - beginIndex) + 1 + 1;
	char* tmp = new char[totalSize];

	for (int i = beginIndex, j = 0; i <= endIndex; i++, j++)
		tmp[j] = mainString[i];

	tmp[totalSize - 1] = '\0';

        // HERE THE CONSTRUCTOR GETS CALLED
	GString result{ tmp };
	delete[] tmp;

        //HERE THE MOVE CONSTRUCTOR GETS CALLED
	return result;
}


The code for the Constructor and the Move Constructor is here: http://pastebin.com/VPSqRPbQ

I really can't spot the problem!

This is the output of the program before crashing: http://prntscr.com/baz2da
Last edited on
I really can't spot the problem!

You have the exact same problem you had earlier. Your constructor calls delete on an uninitialized pointer via Create() -> Clear().
When I start the program in Release mode (Visual C++) nothing happens.

If I switch to Debug mode, that message appears. Scary...

#### by the way ####

This is interesting. Listen up.

When I return result from the function, it's gonna be put inside the COUT statement which will print its mainString variable ( as defined with the operator<< )

1
2
3
4
5
std::ostream& operator<<(std::ostream& s, const GString& other)
{
	std::cout << other.mainString;
	return s;
}


When I return the temporary object result the Move Constructor of my class gets, as expected, called:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
GString::GString(GString&& move)
{
        // this-> can be omitted, I know, but: who is it pointing to?
	this->size = move.size;
	this->mainString = new char[size];

	int i = 0;
	while (i < size)
	{
		mainString[i] = move.mainString[i];
		i++;
	}

	mainString[i] = '\0';

	// Prepare the rvalue to be destroyed
	move.mainString = nullptr;
	move.size = 0;
}


Where move is the variable which I'm returning: result

But the question is: who is the object pointed by the pointer THIS, in this case?

If I had something like

 
GString myString = GString("I'm an rvalue!");


move would be the rvalue and this would be myString.
I have updates my Move Constructor in a better way:

1
2
3
4
5
6
7
8
9
10
GString::GString(GString&& move)
{
	// let's steal the resources
	size = move.size;
	mainString = move.mainString;
        
        // set default values for the rvalue about to be destroyed
	move.size = 0;
	move.mainString = nullptr;
}

Last edited on
1
2
// GString::GString(GString&& move)
GString::GString(GString&& move) noexcept

The compiler can use this information to enable certain optimizations on non-throwing functions ...
For example, containers such as std::vector will move their elements if the elements' move constructor is noexcept, and copy otherwise ... http://en.cppreference.com/w/cpp/language/noexcept_spec
Oh maybe you misunderstood.

When I do

1
2
3
4
GString my{"Hello, dude!"};

// SubString returns a GString by value, hence, a temporary!
cout << my.SubString(0,5);


When SubString returns the value, it calls the Move Constructor.

What is the Move Constructor constructing? A temporary (this) starting from another temporary (move)?

* After the cout, the temporary the Move Constructor has built gets destructed too
Last edited on
> When SubString returns the value, it calls the Move Constructor.

Repeat: Copy-elision (NRVO) may be (typically would be) applied.
http://www.cplusplus.com/forum/general/191860/#msg925444
When copy elision occurs, the implementation treats the source and target of the omitted copy/move operation as simply two different ways of referring to the same object ...
Multiple copy elisions may be chained to eliminate multiple copies.
http://en.cppreference.com/w/cpp/language/copy_elision



Illustration of the importance of the move constructor being noexcept:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
int main()
{
    std::vector<GString> seq ;

    seq.reserve(15) ;
    for( const char* cstr : { "zero", "one", "two", "three", "four", "* copy elision! *" } ) seq.emplace_back(cstr) ;

    std::cout << "*** force the vector relocate objects\n\tis_nothrow_move_constructible? "
              << std::boolalpha << std::is_nothrow_move_constructible<GString>::value
              << "\n\tis_move_constructible? " << std::is_move_constructible<GString>::value << '\n';
    seq.reserve(5000) ;

    std::cout << "\n\n*** substr returns GString by value: copy elision (NRVO) in action\n" ;
    std::cout << "seq.back().substr(2,14): " << seq.back().substr(2,14) << '\n' ;
}


With a noexcept move constructor:
*** force the vector relocate objects
	is_nothrow_move_constructible? true
	is_move_constructible? true
move_construct
move_construct
move_construct
move_construct
move_construct
move_construct


*** substr returns GString by value: copy elision (NRVO) in action
seq.back().substr(2,14): "copy elision!"

http://coliru.stacked-crooked.com/a/e47e734913db0306

Without the noexcept:
*** force the vector relocate objects
	is_nothrow_move_constructible? false
	is_move_constructible? true
copy_construct
copy_construct
copy_construct
copy_construct
copy_construct
copy_construct


*** substr returns GString by value: copy elision (NRVO) in action
seq.back().substr(2,14): "copy elision!"
Hey man, thanks for the exhaustive explanations but it's not what I'm trying to tell you.

I debugged my program and I see the Move Constructor getting called. So, let's put apart the copy elision.

Inside that Move Constructor the argument is the returned value. Who is the *this* pointer? Another temporary to be placed inside the cout <<?
If copy elision is disabled,
1
2
3
4
5
6
7
8
9
10
11
GString GString::SubString(const int beginIndex, const int endIndex) const
{	
        // ...

	GString result{ tmp };

        // ...
	
        return result; // move 'result' into the anonymous return value 
                       // (move construct the value returned from the function)
}


Here's a small program that illustrates what happens when copy elision (NRVO) is disabled:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
#include <iostream>
#include <string>

struct A
{
    explicit A( std::string v ) : v(v) { std::cout << "construct " << *this << '\n' ; }
    ~A() { std::cout << "destroy " << *this << '\n' ; }

    A( A&& that ) : v( std::move(that.v) )
    { std::cout << "move_construct " << *this << "\n\tfrom " << that << '\n' ;  }
    std::string v ;

    friend std::ostream& operator<< ( std::ostream& stm, const A& a  )
    { return stm << "A{" << a.v << "} at address " << std::addressof(a) ; }
};

A bar()
{
    std::cout << "****** in foo ************\n" ;
    A result( "bar_result_123456789" ) ; // long enough to disable small string optimisation
    return std::move(result) ; // disable copy elision
}

int main()
{
    A&& a = bar() ;
    std::cout << "\n****** back in main ************\n" ;
    std::cout << a << '\n' ;
}

****** in foo ************
construct A{bar_result_123456789} at address 0x7fff5439b090
move_construct A{bar_result_123456789} at address 0x7fff5439b060
	from A{} at address 0x7fff5439b090
destroy A{} at address 0x7fff5439b090

****** back in main ************
A{bar_result_123456789} at address 0x7fff5439b060
destroy A{bar_result_123456789} at address 0x7fff5439b060

http://coliru.stacked-crooked.com/a/db5e2e26b9879e92
Haha man you don't get the point XD

I have not disabled the copy-elision, but still the Move Constructor gets called.

I made a real quick video to show you: https://www.youtube.com/watch?v=Zs5khH5T5fM

> I have not disabled the copy-elision, but still the Move Constructor gets called.

Copy elision is permitted, but not mandated by the IS.
Some compilers do not always enable this optimisation (for instance the Microsoft compiler, in debug mode builds).
I have switched from Debug to Release, but still I see the Move Constructor getting called.

Any tips?

Will Copy Elision ever become standard? I mean, will we ever switch from


Some compilers do not always enable this optimisation


to


All compilers always enable this optimisation

Last edited on
> Will Copy Elision ever become standard?

There is a proposal for guaranteed copy elision; but it does not address NRVO.

ISO C++ permits copies to be elided in a number of cases:

. when a temporary object is used to initialize another object (including the object returned by a function, or the exception object created by a throw-expression)
. when a variable that is about to go out of scope is returned or thrown
. when an exception is caught by value

Whether copies are elided in the above cases is up to the whim of the implementation. In practice, implementations always elide copies in the first case, but source code cannot rely on this, and must provide a copy or move operation for such cases, even when they know (or believe) it will never be called.

This paper addresses only the first case. While we believe that reliable NRVO ("named return value optimization", the second bullet) is an important feature to allow reasoning about performance, the cases where NRVO is possible are subtle and a simple guarantee is difficult to give.

http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2015/p0135r0.html
Okay, alright.

So in GCC I should see copy elision, as you said?

Because now, with VC++, I'm having troubles... I think I meet all the requirements for a copy elision to happen
Last edited on
> So in GCC I should see copy elision, as you said?

Yes. Or, if you are using Visual Studio, with the clang++ front-end - 'Clang 3.7 with Microsoft CodeGen'

> Because now, with VC++, I'm having troubles... I think I meet all the requirements for a copy elision to happen

Yes. The Microsoft compiler (with optimisations enabled) consistently elides copies for RVO; but not for NRVO.

RVO:
1
2
3
4
5
6
7
8
9
10
11
12
A bar()
{
    std::cout << "****** in foo ************\n" ;
    return A( "bar_result_123456789" ) ; // test RVO
}

int main()
{
    A&& a = bar() ;
    std::cout << "\n****** back in main ************\n" ;
    std::cout << a << '\n' ;
}

****** in foo ************
construct A{bar_result_123456789} at address 0044FB70

****** back in main ************
A{bar_result_123456789} at address 0044FB70
destroy A{bar_result_123456789} at address 0044FB70

http://rextester.com/MCC72395

No NRVO:
1
2
3
4
5
6
7
8
9
10
11
12
13
A bar()
{
    std::cout << "****** in foo ************\n" ;
    A result( "bar_result_123456789" ) ; // long enough to disable small string optimisation
    return result ; // test NRVO
}

int main()
{
    A&& a = bar() ;
    std::cout << "\n****** back in main ************\n" ;
    std::cout << a << '\n' ;
}

****** in foo ************
construct A{bar_result_123456789} at address 006FFB50
move_construct A{bar_result_123456789} at address 006FFB8C
	from A{} at address 006FFB50
destroy A{} at address 006FFB50

****** back in main ************
A{bar_result_123456789} at address 006FFB8C
destroy A{bar_result_123456789} at address 006FFB8C

http://rextester.com/SCRK80027
Thanks man, you were brilliant =)
it helps enabling optimization in rextester: http://rextester.com/CJVH82561

****** in foo ************
construct A{bar_result_123456789} at address 00E6FB58

****** back in main ************
A{bar_result_123456789} at address 00E6FB58
destroy A{bar_result_123456789} at address 00E6FB58
Why doesn't NRVO work in my program, then?
Why doesn't NRVO work in my program, then?

hard to say without complete, compilable code - this thread meandered quite a bit.
Pages: 123