3

Premise :

#include <iostream>
using namespace std;

class ABC {
 public:

   ABC() {
     cout << "Default constructor ..\n";
   }

   ABC(const ABC& a) {
     cout << "In copy constrcutor ..\n";
   }
   ABC(ABC&& aa) = delete;
};

int main(int argc, char* argv[]) {
  ABC b{ABC{}};
  return 0;
}

Compile this with GCC vs Clang

Clang - Apple LLVM version 8.1.0 (clang-802.0.42)

Gcc - 5.4.0 ubuntu

Observation Clang complains about deleted Move constructor.

Gcc does not complain at all. And will output correctly.

Question Why?

For gcc, I know that if you are simply initializing both lvalue and rvalue, it optimizes and does not actually call the copy constructor and copy the temporary to the lvalue.

Why does Clang differ? I thought (not sure, hence the question) this was in C++ standard, which one deviated (or not)? Or am I doing something wrong.

Compilation command : g++ --std=c++11 -O3 file.cpp

For additional fun, remove curly braces and put parenthesis instead ;)

ABC b{ABC{}}; to, ABC b(ABC());, Unrelated to this question.

EDIT : Someone marked the question as a dup, which it is not. I clearly state that I think C++11 standard includes copy elision. However, CLANG does not seem to have the same behavior on something as critical as constructors.

HERE Is the LINK : http://en.cppreference.com/w/cpp/language/copy_elision

clearly, it says C++11. I trust cppref.

einpoklum
  • 118,144
  • 57
  • 340
  • 684
Pranay
  • 393
  • 4
  • 14
  • 2
    Most vexing parse with parenthesis. – Jarod42 May 17 '18 at 22:38
  • Got compile error in c++11 with both gcc and clang as expected [here](http://coliru.stacked-crooked.com/a/8f8f472bc82ba5b1). – Jarod42 May 17 '18 at 22:40
  • @Jarod42, why do you expect a compiler error? And what error did you expect? – Pranay May 17 '18 at 22:41
  • @Jarod42, I hope you did not replace braces, that would be a different kind of error that we do not want. – Pranay May 17 '18 at 22:43
  • `ABC{}` is a temporary, so `ABC b{ABC{}}` would use a deleted constructor (before C++17). – Jarod42 May 17 '18 at 22:45
  • Yes, it is a temp, but no, it does not use, on gcc, and I specifically set it to C++11. – Pranay May 17 '18 at 22:46
  • Possible duplicate of [Move constructor is required even if it is not used. Why?](https://stackoverflow.com/questions/24678701/move-constructor-is-required-even-if-it-is-not-used-why) – Killzone Kid May 17 '18 at 22:47
  • Bug in your version of gcc, if you upgrade to g++ (GCC) 8.1.0 (or probably for some older ones too), you will got the error. – Jarod42 May 17 '18 at 22:47
  • @KillzoneKid, did you really read the question before marking it as dup? – Pranay May 17 '18 at 22:49
  • @Pranay Did you read the link? And yes I did and the answer is in that link. Move constructor is required because copy elision may or may not happen (until c++17) – Killzone Kid May 17 '18 at 22:50
  • 1
    In my experiments, Gcc complains just like Clang in C++11 and C++14 modes (http://coliru.stacked-crooked.com/a/acbee69d458aa6cc) In C++17 mode both are supposed to accept this code. – AnT stands with Russia May 17 '18 at 22:53
  • For some reason, my gcc works as I described. I mention the version I use as well as the compilation command. It even works correctly and no garbage value or something. Clang complains and I was confused why the same code would make the two have different behavior. – Pranay May 17 '18 at 22:55
  • @einpoklum, jarrod was right, copying from vim to here changed the spacing, I fixed it here. – Pranay May 17 '18 at 22:56
  • @einpoklum: With parenthesis, even clang detect the vexing-parse [Demo](http://coliru.stacked-crooked.com/a/8d7d3c7a3d832ed2). which is the Most in current case if I'm correct. – Jarod42 May 17 '18 at 23:03

2 Answers2

8

ABC{} is a temporary, so ABC b{ABC{}} would use move constructor (even if elision would be possible).

As move constructor is deleted you should get an error.

Your version of gcc has a bug and doesn't detect the error by a wrong elision.

In C++17, with guaranty copy elision, even deleted constructor can be elided. So, your code would compile in C++17 with only one default constructor called.

Jarod42
  • 203,559
  • 14
  • 181
  • 302
  • I'm upgrading my gcc to verify this. Will mark this as correct if it works. – Pranay May 17 '18 at 22:58
  • 1
    I would consider adopting Andrzej Krzemieński's view on how in C++17, it's not copy elision, it's more of [a change to the semantics of rvalues](https://akrzemi1.wordpress.com/2018/05/16/rvalues-redefined/) – einpoklum May 17 '18 at 22:59
  • 1
    I (and AnT) provided link in comment :-) – Jarod42 May 17 '18 at 22:59
1

EDIT : Someone marked the question as a dup, which it is not. I clearly state that I think C++11 standard includes copy elision. However, CLANG does not seem to have the same behavior on something as critical as constructors.

HERE Is the LINK : http://en.cppreference.com/w/cpp/language/copy_elision

clearly, it says C++11. I trust cppref.

It was me who marked it as a dupe. From the page you linked:

Under the following circumstances, the compilers are permitted, but not required to omit the copy- and move- (since C++11)construction

When a nameless temporary, not bound to any references, would be copied or moved (since C++11) into an object of the same type (ignoring top-level cv-qualification), the copy/move (since C++11) is omitted. (until C++17)

This optimization is mandatory; see above. (since C++17)

As you can see move constructor will be required because copy elision is not a requirement but recommendation pre C++17. C++17 compilers should not complain about deleted move constructor under the same circumstance.

Community
  • 1
  • 1
Killzone Kid
  • 6,171
  • 3
  • 17
  • 37
  • 1
    I had to read this twice to understand: emitting the execution move ctor happens after code compiles. So even when not used, you still need it to make it valid code – JVApen May 18 '18 at 07:55