Nissim Levy
Nissim Levy

Reputation: 165

return const value prevent move semantics

I am a beginner in cpp so excuse me for this question.

I was reading that returning const val prevents move semantics. therefore I dont understand why the following code is compiled and works normally. is it because only temporary object is being created? in what cases the move semantics cannot being done? thank you in advance!

#include <iostream>
using namespace std;

const string foo()
{
    return string("hello");
}

int main()
{
    string other = std::move(foo());
}

Upvotes: 3

Views: 1313

Answers (3)

lubgr
lubgr

Reputation: 38285

I was reading that returning const val prevents move semantics. therefore I dont understand why the following code is compiled and works normally.

When move semantics are prevented by some mechanism, this doesn't necessarily mean that the code doesn't compile. Often, it compiles happily, but an expected move construction turns out to be a copy instead.

Example: a type has a user provided copy ctor, which disables compiler-generated move ctors. When we think we move-construct, we don't.

struct Test {
   Test() = default;
   Test(const Test&) {}
};

Test t1;
Test t2{std::move(t1)}; // Copies!

in what cases the move semantics cannot being done?

Coming to your example, something that is const-qualified can't be used to move-construct another object in any meaningful way. Move construction makes sense when resources can be easily transferred, but const-ness prevents that. Example: a type has compiler-generate move and copy constructors, but we can't move-construct from a const instance.

struct Test {
    Test() = default;
};

const Test t1;
Test t2{std::move(t1)};  // Copies!

Besides, it doesn't make sense to move something that is returned by a function by value:

string other = std::move(foo());

When foo() returns by value, you can move-construct from it, unless the return type is const. Hence, to enable move-construction of other:

std::string foo();

string other = foo();

Upvotes: 2

Aykhan Hagverdili
Aykhan Hagverdili

Reputation: 29985

std::move doesn't actually move anything. It is just an "rvalue cast". You cast something to rvalue, and the move constructor / move assignment operator does the actual moving if possible. "If possible" part is the key. In your example the return value is already an rvalue, so std::move literally does nothing. You may even get warnings like "nothing is moved". That is because the move constructor of std::string takes an argument of type std::string&& not const std::string&&. Because of that, the copy constructor is called.

Upvotes: 2

Ashwani
Ashwani

Reputation: 2052

std::move is just a unconditional cast to rvalue. In your case the return value of std::move(foo()) was const std::string&&. And because move constructor does not take const argument, copy constructor was called instead.

struct C {
    C() { std::cout << "constructor" << std::endl; }
    C(const C& other) { std::cout << "copy constructor" << std::endl; }
    C(C&& other) { std::cout << "move constructor" << std::endl; }
};

const C get() {
    return C();
}

int main() {
    C c(std::move(get()));
    return 0;
}

Upvotes: 2

Related Questions