Reputation: 1038
With RVO it is possible to create an object from a returned object without move or copy. Is it possible to do something similar when passing an object to a function?
class SomeClass {
int a;
public:
SomeClass(int _a) {a = _a; std::cout << "created\n";}
SomeClass(const SomeClass& source) {a = source.a; std::cout << "copied\n";}
SomeClass(SomeClass&& source) {a = source.a; std::cout << "moved\n";}
};
void create(SomeClass&& source) {
SomeClass some_object( std::move( source));
//p_some_object = new SomeClass( std::move( source));
//new( p_some_object) SomeClass( std::move( source));
}
// ...
create(SomeClass(15));
In this example the result is "created, moved". (GCC 5.4 / C++11 / -O3) It is the same with new or placement-new. There is no branch or anything so why can't it be created without move?
Upvotes: 0
Views: 206
Reputation: 155507
There is no guarantee that the compiler must inline and analyze enough to recognize that the temporary object referred to by source
is unnecessary. Beyond that, even RVO is intended for value semantics; when &&
references come into play, it's not guaranteed.
As it happens, GCC would handle your case just fine if you followed the general rule for C++ function parameters: "If you need to copy the argument anyway, accept the argument by value."
Accepting by value means that, in cases like this where the argument is constructed as part of the call, no copy or move is needed (it can, and on GCC, is, "RVO"-ed).
class SomeClass {
int a;
public:
SomeClass(int _a) {a = _a; std::cout << "created\n";}
SomeClass(const SomeClass& source) {a = source.a; std::cout << "copied\n";}
SomeClass(SomeClass&& source) {a = source.a; std::cout << "moved\n";}
};
void create(SomeClass some_object) {
}
int main() {
create(SomeClass(15));
}
which if you try it, shows only the created
message.
If the goal is for create
to construct the object for you, then use a templated function with argument forwarding so you construct it within create
, e.g.
template<typename T, class... Args>
T create(Args&&... args) {
T some_object(std::forward<Args>(args)...);
return some_object;
}
int main() {
auto foo = create<SomeClass>(15);
}
which solves the problem by passing the constructor arguments as forwarding references, and constructing the object itself inside create
, avoiding any additional construction, as seen here.
Upvotes: 3
Reputation: 41780
RVO don't happen with reference nor it happen when creating another object from a function parameter.
If you change your reference parameter to a value parameter, RVO will be applied to it, but not the local object:
void create(SomeClass source) {
SomeClass otherObject = std::move(source); // No RVO, object constructed from parameter
}
// ...
create(SomeClass{15}); // RVO applied to source parameter
However, you can achieve it using lambda as function parameters:
template<typename C>
void create(C source) {
SomeClass some_object{source()};
}
// ...
create([]{ return SomeClass{15}; });
You can check the live output
Upvotes: 0