I'm looking at some code, and I see the following function:
template <typename... Args>
static return_t make_return(Args &&... args)
{
// using std::forward<Args> will preserve lvalue args as such, but the point of this function
// is to make a return, where the 99.9+% case is moving a local (lvalue) into the return pack.
// Thus it forces a move, which will move `T&` args (but _not_ `const T&` args) into the
// return pack so that users don't need to type out a bunch of std::moves themselves/
// If they don't want implicit move they can just call std::make_tuple directly
return std::make_tuple(std::move(args)...);
}
The documentation here confuses me.
Why would you explicitly move a forwarding reference?
Wouldn't you want to preserve the lvalue / rvalue within a generic context?
I'm having trouble understanding the rationale or how this behavior would be different from the recommended std::forward.
To put it another way,
I've never seen anyone explicitly opt-out from perfectly-forwarding a forwarding reference.
Does it make sense?
Why would you explicitly move a forwarding reference?
Because it isn't being used for its forwarding properties. Yes, you are correct, we usually std::forward a forwarding reference. But in this case the author used forwarding references solely as a matter of convenience. If it was written as make_return(Args&... args) then it would be impossible to pass an rvalue to make_return, since a non-const lvalue reference may not bind to one.
By using forwarding references, the author allows passing values of any value category into the function, without incurring extra copies if none are needed. The documentation is there to clarify that the function signature isn't for forwarding, but simply for binding to whatever arguments it's given to move out.
A small example reveals the author's intent:
#include <tuple>
#include <iostream>
struct A {
A() { }
A(const A&) { std::cout << "copy\n"; }
A(A&&) { std::cout << "move\n"; }
};
template <typename Arg>
static std::tuple<A> make_return(Arg&& arg) {
return std::make_tuple(std::move(arg));
}
void f(const std::tuple<A>&) { }
void f1() {
std::cout << "return local via make_tuple: ";
A a{};
f(std::make_tuple(a));
}
void f2() {
std::cout << "return local via make_tuple(move): ";
A a{};
f(std::make_tuple(std::move(a)));
}
void f3() {
std::cout << "return local via make_return: ";
A a{};
f(make_return(a));
}
void f4() {
std::cout << "return const via make_tuple: ";
const A a{};
f(std::make_tuple(a));
}
void f5() {
std::cout << "return const via make_tuple(move): ";
const A a{};
f(std::make_tuple(std::move(a)));
}
void f6() {
std::cout << "return const via make_return: ";
const A a{};
f(make_return(a));
}
int main() {
f1();
f2();
f3();
f4();
f5();
f6();
}
Output:
return local via make_tuple: copy
return local via make_tuple(move): move
return local via make_return: move
return const via make_tuple: copy
return const via make_tuple(move): copy
return const via make_return: copy
In cases where a local non-const variable is returned, we want to std::move its contents. This is achievable using std::make_tuple(std::move(a)), because a plain std::make_tuple(a) would copy. To save some typing, the author wrote make_return as a shorthand for std::make_tuple(std::move(a)): the example shows that f3 works just like f2.
When a constant is passed, std::move won't make any difference, but no harm either. So we could use std::make_tuple, but make_return works just fine, too. Cases f4, f5, f6 all behave the same, showing that one doesn't really need to think twice before mixing constants and non-constants in make_return (in the case of multiple entries constituting return_t).
What remains is moving a non-const variable that is not local to the function, and thus we wouldn't like to destroy its contents. In these cases make_return is unwanted and one would need to resort back to manual invocation of std::make_tuple (utilizing std::move where appropriate only).
Now what would this look like with std::forward? Changing the definition of make_return to utilizing
std::make_tuple(std::forward<Arg>(arg));
produces:
return local via tuple: copy
return local via tuple(move): move
return local via make_return: copy
return const via tuple: copy
return const via tuple(move): copy
return const via make_return: copy
since a in f3 gets passed as a const A&. Indeed, make_return is then, by the logic of forwarding, a mere synonyme for std::move, losing any benefit we hoped to achieve.
make_return() is to return a tuple using a value, since this value won't be needed anymore, as is used in make_return (end scope of the a function), there's no need to use std::forward<>, since it could forward lvalue references which incurs in copies (depending on implementation), but value is at the end of scope so is not needed to preserve any resources.
Forcing std::move on the make_tuple, forces to use rvalue references first, omitting possible more overhead (depending on implementation).
Related
I'm writing a simple game with Entity Component System. One of my components is NativeScriptComponent. It contains the instance of my script. The idea is that I can create my NativeScriptComponent anytime and then Bind to it any class implementing Scriptable interface. After that my game's OnUpdate function will automatically instantiate all scripts in my game and will call their OnUpdate function.
My scripts can have their own, different constructors so I need to forward all the arguments to them when I bind my script.
Consider the following code:
#include <iostream>
#include <memory>
#include <functional>
using namespace std;
struct Scriptable
{
virtual void OnUpdate() {};
};
struct MyScript : Scriptable
{
MyScript(int n) : value(n) {}
void OnUpdate() override {cout << value;}
int value;
};
struct NativeScriptComponent
{
unique_ptr<Scriptable> Instance;
function<unique_ptr<Scriptable>()> Instantiate;
template<typename T, typename ... Args>
void Bind(Args&&... args)
{
// (A)
Instantiate = [&args...]() { return make_unique<T>(forward<Args>(args)...); };
// (B) since C++20
Instantiate = [...args = forward<Args>(args)]() { return make_unique<T>(args...); };
}
};
int main()
{
NativeScriptComponent nsc;
nsc.Bind<MyScript>(5);
// [..] Later in my game's OnUpdate function:
if (!nsc.Instance)
nsc.Instance = nsc.Instantiate();
nsc.Instance->OnUpdate(); // prints: 5
return 0;
}
1) What is the difference between option A and B
Instantiate = [&args...]() { return make_unique<T>(forward<Args>(args)...); };
vs
Instantiate = [...args = forward<Args>(args)]() { return make_unique<T>(args...); };
Why can't I use forward<Args>(args) inside make_unique in option B?
Are both A and B perfectly-forwarded?
For added completeness, there's more things you can do. As pointed out in the other answer:
[&args...]() { return make_unique<T>(forward<Args>(args)...); };
This captures all the arguments by reference, which could lead to dangling. But those references are properly forwarded in the body.
[...args=forward<Args>(args)]() { return make_unique<T>(args...); };
This forwards all the arguments into the lambda, args internally is a pack of values, not references. This passes them all as lvalues into make_unique.
[...args=forward<Args>(args)]() mutable { return make_unique<T>(move(args)...); };
Given that, as above, args is a pack of values and we're creating a unique_ptr anyway, we probably should std::move them into make_unique. Note that the lambda has its own internal args here, so moving them does not affect any of the lvalues that were passed into this function.
[args=tuple<Args...>(forward<Args>(args)...)]() mutable {
return std::apply([](Args&&... args){
return std::make_unique<T>(forward<Args>(args)...);
}, std::move(args));
};
The most fun option. Before we'd either capture all the arguments by reference, or forward all the args (copying the lvalues and moving the rvalues). But there's a third option: we could capture the lvalues by reference and move the rvalues. We can do that by explicitly capturing a tuple and forwarding into it (note that for lvalues, the template parameter is T& and for rvalues it's T - so we get rvalues by value).
Once we have the tuple, we apply on it internally - which gives us Args&&... back (note the && and that this is not a generic lambda, doesn't need to be).
This is an improvement over the previous version in that we don't need to copy lvalues -- or perhaps it's worse because now we have more opportunity for dangling.
A comparison of the four solutions:
option
can dangle?
lvalue
rvalue
&args...
both lvalues and rvalues
1 copy
1 move
...args=FWD(args) and args...
no
2 copies
1 move, 1 copy
...args=FWD(args) and move(args)...
no
1 copy, 1 move
2 moves
args=tuple<Args...>
lvalues
1 copy
2 moves
A captures the arguments by reference and then perfectly forwards into make_unique. Unfortunately by that point the references are (probably) dangling, so calling Instantiate in your example has undefined behaviour.
B perfectly forwards the arguments into the data members of the closure object, then copies those data members into make_unique. You can't forward here without marking the lambda mutable, because the body of a lambda is a const member function of the closure object.
A can't forward rvalues, the references to them dangle. B can't forward lvalues, it does a copy.
If you only want to instantiate one Scriptable, then you can mark the lambda in B as mutable, and move in there. If you want to construct multiple Scriptables, you will have to do some copying.
About std::move, here is what I can interpret, according to http://en.cppreference.com/w/cpp/utility/move :-
If I want to transfer ownership, I have to call std::move (or in rare case, std::forward).
Responsibility of std::move is calling operator=(A&& other).
The most essential step of the move operation is supposed to be implemented in operator=(A&&).
It is tricky to ensure that operator=(A&&) would be called. It need a special converter.
There are only two converters in the C++ world that can convert variables into xvalue (the &&) : std::move and std::forward.
Question
After adding many of std::move(std::unique_ptr) in my code, I start to worry that for such basic feature like transfer ownership, I have to heavily rely on the standard library (std::).
Do I really have to use std::move to transfer ownership?
Is spamming and hard-code calling std::move in many places of code-base a correct way to go for a high-standard program?
Should std::move be encapsulated?
They are actually a single question, but ask in different perspectives.
Edit
As request, here is my trial & error. It compiled ok.
I have no problem about the code, but I worry about its approach / pattern.
https://ideone.com/y8Pcgf
class T{
public: int value;
public: T(int a=1234){
value = a;
}
};
int main() {
std::unique_ptr<T> t1 = std::unique_ptr<T>(new T(1));
void* databaseNew=operator new [](sizeof(std::unique_ptr<T>));
std::unique_ptr<T>* t1ptr=static_cast<std::unique_ptr<T>*>(databaseNew);
new (t1ptr) std::unique_ptr<T>(std::move(t1));
return 0;
}
Rule of thumb:
If you're in a deduced x-value context, use std::forward:
template<class T>
void foo(T&& t) // T is deduced x-value, so we forward it
{
bar(std::forward<T>(t));
}
Otherwise use std::move
template<class T>
void foo1(std::vector<T> v) // although vector<T> is deduced, it's not an x-value
{
bar(std::move(v)); // so move it
}
template<class T>
void foo2(std::vector<T>&& v) // although vector<T> is deduced, it's not an x-value.
// In this case an r-value reference
{
bar(std::move(v)); // so move it
}
template<class T>
void foo3(std::vector<T>& v) // although vector<T> is deduced, it's not an x-value.
// In this case an l-value reference
{
bar(std::move(v)); // so move it
}
void foo4(std::vector<int> v) // complete type
{
bar(std::move(v)); // so move it
}
void foo5(std::vector<int> const & v) // const reference
{
bar(v); // not much point in moving it. std::move would cast it
// to std::vector<int> const&&, which although is detectable
// decays to std::vector<int> const&
}
which although is detectable... what?
It is permissible, if not necessarily advisable to write code like this:
#include <iostream>
struct X
{
void foo() const &
{
// do one thing...
std::cout << "one thing\n";
}
void foo() const &&
{
// do something else...
std::cout << "or another\n";
}
};
int main()
{
const X x;
x.foo();
std::move(x).foo();
}
const r-value references do exist, it's just that no-one uses them because there is no reasonable use-case.
The need to explicitly move, of which you complain, was actually done on purpose. Before unique_ptr, STL had a horrid construct called auto_ptr. It would move ownership impllicitly, and was borderline unusable unless you really really really knew what you were doing.
To make things more usable, in most cases C++ now requires you to explicitly state that you intend on moving ownership over a container, by using std::move.
In fact, std::move is little more than a cast to an rvalue reference.
There are cases where such an explicit specification is not necessary. For example, if the container from which you take ownership is already an rvalue (e.g. - a temporary object), then no case using std::move is necessary. For example, the following doesn't compile:
std::unique_ptr<int> a;
a = new int;
But the following does, without needing a move:
std::unique_ptr<int> a;
a = std::unique_ptr<int>(new int);
The reason this does not need a call to std::move, despite invoking the move operator, is that the object we move the ownership away from is already a temporary object (i.e. - an rvalue), so no cast is necessary.
Another example is if you call a function that returns a unique_ptr. You might have to call std::move inside the function to get it into the return value, but you do not need to call std::move on the function's return value to get it into the outside unique_ptr. It is already an rvalue, and therefor no cast is necessary.
I'm trying to figure out is it possible to avoid calling the copy-constructor when passing a just-created object to a function? I don't want to use this object any more, I'd just like to name the temporary and pass it by value to some other function.
I know that in C++11 I can just move this object, but I'm concerned how to improve this in C++03. So essentially, I'm interested in eliminating the copy-constructor call in the code below:
Snippet:
#include <iostream>
struct Foo
{
Foo(void) { std::cout << "Default ctr\n"; };
Foo(const Foo& f) { std::cout << "Copy ctr\n"; }
};
void bar(Foo f)
{
std::cout << "Inside bar\n";
}
int main()
{
Foo f;
bar(f);
bar(Foo());
}
Possible result:
Default ctr
Copy ctr
Inside bar
Default ctr
Inside bar
Compilation string:
g++ -O3 -Wall -pedantic main.cpp && ./a.out
I.e. to use some kind of copy-elision but on an argument passed to a function, not on a return value (NRVO).
Pass by value implies copying. This copying can be elided but semantically pass by value is still copying. You can pass by reference as well to avoid this copy. Note that these are the only situations in which copy elision are permitted:
in a return statement
in a throw-expression
with a temporary that hasn't been bound to a reference
and another to do with exceptions
That's why in:
bar(f);
bar(Foo());
only the second involves copy elision. Pre-C++11, just use a reference or use Boost.
Use this syntax:
void bar(const Foo& f)
{
std::cout << "Inside bar\n";
}
The above code passes the object as reference. This effectively copies the address of the object only (like a pointer) but the reference can be handled like the original object.
You can use reference(or pointer) of class Foo as parameter to function bar(). This will avoid creation of temporary object, from the object passed as argument to function bar(),and hence no copy constructor will be called.
While working on this question, I noticed that GCC (v4.7)'s implementation of std::function moves its arguments when they are taken by value. The following code shows this behavior:
#include <functional>
#include <iostream>
struct CopyableMovable
{
CopyableMovable() { std::cout << "default" << '\n'; }
CopyableMovable(CopyableMovable const &) { std::cout << "copy" << '\n'; }
CopyableMovable(CopyableMovable &&) { std::cout << "move" << '\n'; }
};
void foo(CopyableMovable cm)
{ }
int main()
{
typedef std::function<void(CopyableMovable)> byValue;
byValue fooByValue = foo;
CopyableMovable cm;
fooByValue(cm);
}
// outputs: default copy move move
We see here that a copy of cm is performed (which seems reasonable since the byValue's parameter is taken by value), but then there are two moves. Since function is operating on a copy of cm, the fact that it moves its argument can be seen as an unimportant implementation detail. However, this behavior causes some trouble when using function together with bind:
#include <functional>
#include <iostream>
struct MoveTracker
{
bool hasBeenMovedFrom;
MoveTracker()
: hasBeenMovedFrom(false)
{}
MoveTracker(MoveTracker const &)
: hasBeenMovedFrom(false)
{}
MoveTracker(MoveTracker && other)
: hasBeenMovedFrom(false)
{
if (other.hasBeenMovedFrom)
{
std::cout << "already moved!" << '\n';
}
else
{
other.hasBeenMovedFrom = true;
}
}
};
void foo(MoveTracker, MoveTracker) {}
int main()
{
using namespace std::placeholders;
std::function<void(MoveTracker)> func = std::bind(foo, _1, _1);
MoveTracker obj;
func(obj); // prints "already moved!"
}
Is this behavior allowed by the standard? Is std::function allowed to move its arguments? And if so, is it normal that we can convert the wrapper returned by bind into a std::function with by-value parameters, even though this triggers unexpected behavior when dealing with multiple occurrences of placeholders?
std::function is specified to pass the supplied arguments to the wrapped function with std::forward. e.g. for std::function<void(MoveTracker)>, the function call operator is equivalent to
void operator(CopyableMovable a)
{
f(std::forward<CopyableMovable>(a));
}
Since std::forward<T> is equivalent to std::move when T is not a reference type, this accounts for one of the moves in your first example. It's possible that the second comes from having to go through the indirection layers inside std::function.
This then also accounts for the problem you are encountering with using std::bind as the wrapped function: std::bind is also specified to forward its parameters, and in this case it is being passed an rvalue reference resulting from the std::forward call inside std::function. The function call operator of your bind expression is thus forwarding an rvalue reference to each of the arguments. Unfortunately, since you've reused the placeholder, it's an rvalue reference to the same object in both cases, so for movable types whichever is constructed first will move the value, and the second parameter will get an empty shell.
I have tried the following:
std::function<void ()> getAction(std::unique_ptr<MyClass> &&psomething){
//The caller given ownership of psomething
return [psomething](){
psomething->do_some_thing();
//psomething is expected to be released after this point
};
}
But it does not compile. Any ideas?
UPDATE:
AS suggested, some new syntax is required to explicitly specify we need to transfer the ownership to the lambda, I am now thinking about the following syntax:
std::function<void ()> getAction(std::unique_ptr<MyClass> psomething){
//The caller given ownership of psomething
return [auto psomething=move(psomething)](){
psomething->do_some_thing();
//psomething is expected to be released after this point
};
}
Would it be a good candidate?
UPDATE 1:
I will show my implementation of move and copy as following:
template<typename T>
T copy(const T &t) {
return t;
}
//process lvalue references
template<typename T>
T move(T &t) {
return std::move(t);
}
class A{/*...*/};
void test(A &&a);
int main(int, char **){
A a;
test(copy(a)); //OK, copied
test(move(a)); //OK, moved
test(A()); //OK, temporary object
test(copy(A())); //OK, copying temporary object
//You can disable this behavior by letting copy accepts T &
//test(move(A())); You should never move a temporary object
//It is not good to have a rvalue version of move.
//test(a); forbidden, you have to say weather you want to copy or move
//from a lvalue reference.
}
This issue is addressed by lambda generalized capture in C++14:
// a unique_ptr is move-only
auto u = make_unique<some_type>(some, parameters);
// move the unique_ptr into the lambda
go.run([u = move(u)]{do_something_with(u);});
You cannot permanently capture a unique_ptr in a lambda. Indeed, if you want to permanently capture anything in a lambda, it must be copyable; merely movable is insufficient.
This could be considered a defect in C++11, but you would need some syntax to explicitly say that you wanted to move the unique_ptr value into the lambda. The C++11 specification is very carefully worded to prevent implicit moves on named variables; that's why std::move exists, and this is a good thing.
To do what you want will require either using std::bind (which would be semi-convoluted, requiring a short sequence of binds) or just returning a regular old object.
Also, never take unique_ptr by &&, unless you are actually writing its move constructor. Just take it by value; the only way a user can provide it by value is with a std::move. Indeed, it's generally a good idea to never take anything by &&, unless you're writing the move constructor/assignment operator (or implementing a forwarding function).
The "semi-convoluted" solution using std::bind as mentioned by Nicol Bolas is not so bad after all:
std::function<void ()> getAction(std::unique_ptr<MyClass>&& psomething)
{
return std::bind([] (std::unique_ptr<MyClass>& p) { p->do_some_thing(); },
std::move(psomething));
}
A sub-optimal solution that worked for me was to convert the unique_ptr to a shared_ptr and then capture the shared_ptr in the lambda.
std::function<void()> getAction(std::unique_ptr<MyClass> psomething)
{
//The caller given ownership of psomething
std::shared_ptr<MyClass> psomethingShared = std::shared_ptr<MyClass>(std::move(psomething));
return [psomethingShared]()
{
psomethingShared->do_some_thing();
};
}
I used this really dodgy workaround, which involves sticking the unique_ptr inside a shared_ptr. This is because my code required a unique_ptr (due to an API restriction) so I couldn't actually convert it to a shared_ptr (otherwise I'd never be able to get my unique_ptr back).
My justification for using this abomination is that it was for my test code, and I had to std::bind a unique_ptr into the test function call.
// Put unique_ptr inside a shared_ptr
auto sh = std::make_shared<std::unique_ptr<Type>>(std::move(unique));
std::function<void()> fnTest = std::bind([this, sh, input, output]() {
// Move unique_ptr back out of shared_ptr
auto unique = std::move(*sh.get());
// Make sure unique_ptr is still valid
assert(unique);
// Move unique_ptr over to final function while calling it
this->run_test(std::move(unique), input, output);
});
Now calling fnTest() will call run_test() while passing the unique_ptr to it. Calling fnTest() a second time will result in an assertion failure, because the unique_ptr has already been moved/lost during the first call.
One also need to know, that lambdas capturing unique_ptr cannot be converted into std::function because std::function requires that the callable object is copyable.
auto lambdaWithoutCapture = [](){return 1;}; //Can be std::function
auto lambdaWithCapture = [=](){return 1;}; //Can be std::function
auto lambdaWithCapture2 = [&](){return 1;}; //Can be std::function
auto lambdaWithCapture3 = [uptrProblematic = std::move(uptrProblematic)]() mutable {return 1;}; //Can't be std::function
Therefore, if you don't have to specify return type of the function, you can use such approach which does not use std::function. But you need to know, that this will only work in local scope. You can't declare auto workerFactory(); in header file, as this will raise compilation error.
auto workerFactory()
{
std::unique_ptr uptrProblematic = std::make_unique<int>(9);
int importantData = 71;
return [=, uptrProblematic = std::move(uptrProblematic)](std::string input) mutable -> int {
std::cout << "Problematic variable is equal to: " << *uptrProblematic << "\n";
std::cout << "Important data is equal to: " << importantData << "\n";
std::cout << "Input equal to: " << input << "\n";
return 9;
};
}
int main()
{
auto worker = workerFactory();
worker("Test");
}