C++ Logo


Advanced search

Re: C++20 unexpectedly calling const overload of constructor

From: Rob Lefebvre <rob_lefebvre_at_[hidden]>
Date: Thu, 12 Mar 2020 11:43:20 +0000
On Wednesday, 11 March 2020 Thiago Macieira via Std-Discussion
>> class MyClass
>> {
>> public:
>> MyClass(const char (&value)[30])
>> {
>> std::cout << "CONST " << value << '\n';
>> }
>> MyClass(char (&value)[30])
>> {
>> std::cout << "NON-CONST " << value << '\n';
>> }
>> };
>> MyClass test_1()
>> {
>> char buf[30] = "test_1";
>> return buf;
>> }
>Your finding that the RVALUE constructor gets called is the hint. This is
>happening because in the function above, by the time that the constructor for
>MyClass is called, buf is an xvalue. If you change it to:
>MyClass test_1()
> char buf[30] = "test_1";
> MyClass ret(buf);
> return ret;
Thank you for the reply, Thiago. Yes, this is not a surprise as buf can no longer be moved in the MyClass Construction. The stack overflow discussion is a bit more detailed and shows essentially this case, that return buf; and return {buf}; call two different constructors. The difference between buf and {buf} is well understood in the presence of a move constructor. With the new standard, it becomes different even without the presence of move constructor.
>Note this was already the case for:
>std::string foo()
> std::string s;
> getString(&s);
> return s; // implicitly moved from
>At the point of the return, variable s was going out of scope, so it's
>implicitly an xvalue and you got the std::string(std::string &&) move
>constructor. Note that this became stronger with NRVO, but the principle
>applies even when the optimisation can't kick in.
Yes, foo()'s return can implicitly move. No argument there. The problem is in the case where there is no move constructor, but there are constructors that take both T& and const T&. I think that this language change will break a fair amount of code. The problem is in 15.8.3. The C++20 standard change (already implemented in some compilers for C++11/14/17 mode!) is that move construction is considered first (not new), even when there is no move constructor in the class (new).
In my opinion, the removal of "or if the type of the first parameter of the selected constructor is not an rvalue reference to the object's type" is too strong of a fix for NRVO. Perhaps there can be a less all-encompassing fix that will preserve NRVO improvements but will not break existing code?
Rob Lefebvre

Received on 2020-03-12 06:46:09