1

This is my class and my program:

class A { public: int x; int* v; A() : x(5), v(new int[5]) {cout<<"default ctor. "; for (int i=0; i<x; i++) v[i]=rand()%10;} A(const A& a) : x(a.x), v(new int[x]) {cout<<"copy ctor. "; for (int i=0; i<x; i++) v[i]=a.v[i]; } A(A&& a) : x(a.x), v(a.v) {cout<<"move ctor. ";a.x=0;a.v=0;} A& f() {return *this;} A g() {return *this;} }; int main() { srand(time(0)); cout<<"A a --> ";A a;cout<<endl; cout<<"A b(A()) --> ";A b(A());cout<<endl; cout<<"A c(a.f()) --> ";A c(a.f());cout<<endl; cout<<"A d(a.g()) --> ";A d(a.g());cout<<endl; cout<<"A e(A().g()) --> ";A e(A().g());cout<<endl; } 

I would expect the objects b, d and e to use the move constructor, however the output I get is:

A a --> default ctor.

A b(A()) -->

A c(a.f()) --> copy ctor.

A d(a.g()) --> copy ctor.

A e(A().g()) --> default ctor. copy ctor.

If in all three cases r-values are the arguments of the constructors, why isn't the move constructor being used?

Obviously I am compiling with the -std=c++11 option.

1 Answer 1

4

With b, you've hit the "most vexing parse." b is a function declaration, not an object.

Take the expression

A b(A()); 

Let's do some name changes and semantic-preserving transformations:

int foo(int (*p)()); 

Now, I think it's pretty clear that foo is a function (returns int and takes pointer to int() function as parameter). Now substitute back A for int, b for foo and remember functions get implicitly converted to pointer-to-function. You'll see it's the same thing.

To get around this, you can use brace initialisation (thanks to @MikeSeymour for pointing that out):

A b{A()}; 

With d, I think you're the "victim" of copy elision. The "copy ctor" output is probably from initialising the return value with *this; the move from the return value to d is completely elided.

With e, it's the same story as d.

Sign up to request clarification or add additional context in comments.

4 Comments

It might be worth mentioning how to avoid the vexing parse: A b{A()}; or A b((A()));
@MikeSeymour Thanks, I never remember that, as I'm stuck with VS2010 and thus no brace-init :-(
it would have never occured to me that I was declaring a function. Also, I compiled with the -fno-elide-constructors option and now I can see the move constructors are indeed being used. It's hard to get to know how the language works when the compiler does bad things (meaning, unexpected behaviour) with good purpose.
@dabadaba If you didn't have -fno-elide-constructors and you really wanted to know for sure which initializations use the move constructor, you could simply declare it as deleted: A(A&&) = delete;. Then instead of copy/move elision messing with you, you'd get a convenient error message.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.