clang: copy elision warning

In recent times I've taken to writing code like:
1
2
3
4
5
std::string assing(const zmq::message_t& msg)
{
    std::string str((const char*)msg.data(), msg.size());
    return std::move(str);  // warning here
}


With clang 3.7.1, I now get: "warning: moving a local object in a return statement prevents copy elision [-Wpessimizing-move]"

Now, in this case, it's clear that return value optimization can be applied. But there's no guarantee.

I was working on some code just last week on a 4.something GNU C++ compiler where we had to use std::move, because the compiler chose to copy the object.

Do you think specifying std::move in this case is wrong? Is there a better way? What if the compiler doesn't apply copy elision?
It is always wrong to write return std::move(anything);

Exactly as clang says, because this forbids copy elision. It may be obvious to you, and perhaps it could make sense to poke at the compiler devs to make it an extension to the language, but the spec says no.

Simple return str; is guaranteed to move, not copy, out of str if (as unlikely as it is) it cannot apply copy elision.

copy elision rules: http://en.cppreference.com/w/cpp/language/copy_elision
return statement spec: http://en.cppreference.com/w/cpp/language/return#Notes

PS: there is a proposal to make copy elision a requirement, p0135, it was approved by evolution, (so standard committee is generally in favor) but not yet submitted to core so not making it to C++17 cutoff
Last edited on
The case I had recently was trying to build an initializer list to pass to an object. I can't remember the details (I can dig out the example for you), but we had to explicitly force a move because the compiler was doing the wrong thing. The copy caused a recursion in the constructor.

In the trivial case of returning a local string, it may not matter. But we often are dealing with more complicated structures and we have can't leave it to the compiler to decide. How can we be sure the compiler will do the move if it can do the move?
Last edited on
When compiling the return statement, the compiler can decide between copy elision and move (at least until that proposal passes), but it cannot decide between move and copy: the return statement is a call to the move constructor if there is one.
the return statement is a call to the move constructor if there is one.
I understand that.

In my experience, the compiler doesn't always copy when it can. And when you really need it to, (i.e. it must not do a copy) it can't be left to chance, hence the explicit move.

I'll take a look at that code (it was written by someone else) and make stripped down version that replicates the problem.
Last edited on
Forgive me in advance for providing a 1 cent idea in addition to the vast experience / knowledge of those already here. I feel like I am having quite a bit of nerve in doing this, so If this is not helpful, then let me know and I will bug out :+) Edit: I guess the only reason I am posting is that I may have stumbled on an unfortunate sequence of events.

It seems to me that: for copying to take place, both forms of copy elision and move have to have been disabled. So that's not a new idea, but maybe the copy elision isn't happening just because of this:

cppreference wrote:

http://en.cppreference.com/w/cpp/language/copy_elision

Notes
Copy elision is the only allowed form of optimization that can change the observable side-effects. Because some compilers do not perform copy elision in every situation where it is allowed (e.g., in debug mode), programs that rely on the side-effects of copy/move constructors and destructors are not portable.


Underlining emphasis mine.

I then wonder if this is a problem:

cppreference wrote:
Notes

Types without a move constructor, but with a copy constructor that accepts const T& arguments, satisfy std::is_move_constructible.

Move constructors are usually noexcept, since otherwise they are unusable in any code that provides strong exception guarantee.

In many implementations, is_nothrow_move_constructible also checks if the destructor throws because it is effectively noexept(T(arg)). Same applies to is_trivially_move_constructible, which, in these implementations, also requires that the destructor is trivial: GCC bug 51452 LWG issue 2116.

http://en.cppreference.com/w/cpp/types/is_move_constructible

It might be a problem because there was some discussion about a bug in gcc4.6 (kbw mentioned using gcc 4.x) coming from the ctor's and dtor both not being noexcept. Maybe this was fixed in gcc4.7 ?

Just for giggles, could you make a call to std::is_move_constructible before the return statement to maybe provide a clue as to what is really happening there? Edit: Maybe this was a problem then, but not now: ctor & dtor are supposed to always be noexcept to provide the strong guarantee

So to summarise: maybe gcc4.6 was being used in debug mode, and this led to copy elision and the bug meant move being disabled, which led to the copy.

Also, was it proved that clang3.7 was doing a copy? And I discovered that -Wpessimizing-move is specific and new feature for clang++, and is included in -Wall.

Again, let me know if replying to this in any detail is going to be a waste of your time.
Last edited on
The code that had the problem has been fixed and the move is no longer necessary. I'm having difficulty in finding the version that had the problem.

The problem showed up on GCC 4.8.2.

Ok, no explicit moves, I'll let the compiler do the work and hope it does the right thing.
Actually std::move(...) does nothing but add the move semantic, so that it can be passed to a function that requires the move semantic (e.g. func(val_type &&val)). std::move() as a return type doesn't make sense.
std::move is implemented as a type cast, but that doesn't mean it does nothing. It's implementation is irrelevant, what's important is that it invokes the object's move semantics.
Topic archived. No new replies allowed.