2

Following code works perfectly fine (showing RVO):

struct A { 
  A (int) { cout << "A::A()\n"; }  // constructor
  A (const A&) { cout << "A::A(const A&)\n"; }  // copy constructor
};

A foo () { return A(0); }

int main () {
  A a = foo();
}

Output:

A::A()  // --> which means copy constructor is not called

If I mark the copy constructor as explicit:

explicit A (const A&) { ... }

Then the compiler errors out:

explicit.cpp: In function ‘A foo()’:
explicit.cpp:10:22: error: no matching function for call to ‘A::A(A)’
 A foo () { return A(0); }
                      ^
explicit.cpp:5:3: note: candidate: A::A(int)
   A (int) { cout << "A::A()\n"; }
   ^
explicit.cpp:5:3: note:   no known conversion for argument 1 from ‘A’ to ‘int’
explicit.cpp: In function ‘int main()’:
explicit.cpp:14:13: error: no matching function for call to ‘A::A(A)’
   A a = foo();
             ^
explicit.cpp:5:3: note: candidate: A::A(int)
   A (int) { cout << "A::A()\n"; }
   ^
explicit.cpp:5:3: note:   no known conversion for argument 1 from ‘A’ to ‘int’

Why is it happening, Shouldn't the RVO work as it is?

iammilind
  • 68,093
  • 33
  • 169
  • 336
  • 1
    This has got nothing to do with RVO. – Konrad Rudolph Aug 02 '16 at 12:21
  • Looks like this answers it: http://stackoverflow.com/questions/29472565/explicit-copy-constructor-compile-error – NathanOliver Aug 02 '16 at 12:23
  • @KonradRudolph, But without `explicit` keyword, the RVO is taking place and not making multiple copies of `A`. Why shouldn't the same continue when we mark the constructor as `explicit`? The Q may not have directly to do with RVO, but I found some indirect linking between the two things. @NathanOliver, thanks for pointing out. That Q is showing the observation. However this Q is about "Why?" – iammilind Aug 02 '16 at 12:23
  • And that was answered in the comment right below the answer. I guess that doesn't really make it a dupe but meh. – NathanOliver Aug 02 '16 at 12:25

1 Answers1

8

RVO can elide a copy, but the language rules require that a copy (or a move) must still be possible:

[C++14: 12.8/31]: When certain criteria are met, an implementation is allowed to omit the copy/move construction of a class object, even if the constructor selected for the copy/move operation and/or the destructor for the object have side effects. [..]

[C++14: 12.8/32]: [..] [ Note: This 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. —end note ]

You made the copy impossible by adding explicit, and a move is not possible because your copy constructor blocks the creation of an implicitly-defined move constructor.

You could allow a move instead by adding your own move constructor, perhaps a defaulted one:

A(A&&) = default;

But this is only another way of abiding by the same language rule.

C++17 will relax the rule somewhat anyway, by adding some guarantees of copy elision which will not be subject to this constraint.

Community
  • 1
  • 1
Lightness Races in Orbit
  • 378,754
  • 76
  • 643
  • 1,055