I'm trying to understand the implications of capturing variables by reference or by value in the following example use of a lambda expression.
/** Encapsulates the private implementation details of Thread. */
struct Thread::Impl
{
public:
Impl() : m_queue(), m_thread()
{
}
private:
Impl(const Impl&);
const Impl& operator=(const Impl&);
void run()
{
// do some work
}
std::unique_ptr<std::thread> m_thread;
friend class Thread;
};
Thread::Thread() : m_impl(new Impl())
{
// start the thread
// ************************************
// '=' or '&' in the the capture block?
// ************************************
m_impl->m_thread = std::unique_ptr<std::thread>(new std::thread( [&]{ m_impl->run(); } ));
}
Regardless of whether I use & or = in the capture block the above code works OK. So which should I use?
If I use [&], m_impl is captured by reference, right?
If I use [=] m_impl is captured by value, right? But I don't understand why it compiles. What is it making a copy of? The copy ctor for Impl is disabled.
If I use [&], m_impl is captured by reference, right? If I use [=] m_impl is captured by value, right?
Neither of those is true. m_impl isn't captured at all. In both cases, the this is captured. But since the thread is a member variable of the object whose this pointer your capturing, this (ha!) is safe.
Use whichever you prefer. Or [this], if you want to be more explicit.
Related
Look at the second answer here:
What is the need for enable_shared_from_this?
it says:
"Short answer: you need enable_shared_from_this when you need to use inside the object itself existing shared pointer guarding this object.
Out of the object you can simply assign and copy a shared_ptr because you deal with the shared_ptr variable as is."
and later down in the last line it says:
"And when and why one can need a shared pointer to this instead of just this it is quite other question. For example, it is widely used in asynchronous programming for callbacks binding."
Here in this post I want to ask exactly this other question. What is an use case for "enable_shared_from_this" and "shared_from_this"?
A simple use-case would be to ensure this survives till the end of some asynchronous, or delayed operation:
class My_type : public std::enable_shared_from_this<My_type> {
public:
void foo() {}
void perform_foo() {
auto self = shared_from_this();
std::async(std::launch::async, [self, this]{ foo(); });
}
};
boost::asio uses this technique a lot in their examples:
https://www.boost.org/doc/libs/1_66_0/doc/html/boost_asio/example/cpp11/allocation/server.cpp
I have the following code sample:
void MyClass::Register2(std::string name, std::string email)
{
m_name = std::move(name);
m_email = std::move(email);
}
void MyClass::Register1(std::string name)
{
Register2(std::move(name), "invalid_email");
}
My questions are:
Do I need to use std::move when calling Register2() from Register1()?
Do I need to call std::move() inside Register1()?
If the answer for question 2. is yes, would be possible to have a dedicated operator instead?
For example:
void MyClass::Register2(std::string name, std::string email)
{
m_name <= name; // perform move
m_email <= email; // perform move
}
Do I need to use std::move when calling Register2() from Register1()?
Yes, because name is an lvalue and you want to turn it into an rvalue.
Do I need to call std::move() inside Register1()?
Yes, for the same reason as above.
If the answer for question 2. is yes, would be possible to have a dedicated operator instead?
It would be possible, but I do not think it has been proposed. Also, if it were to be proposed, I do not think it would be accepted as it doesn't bring much value over std::move.
Yes
Yes
No
std::move looks something like this
template<typename T>
std::remove_reference_t<T>&& move(T&& value) {
return static_cast<std::remove_reference_t<T>&&>(value);
}
All it does is it casts the thing you pass as the argument to an rvalue. This step is essential because arguments, even if you pass an rvalue, are always lvalues (because they have names). Lvalues are not moveable, therefore if you don't cast them to an rvalue, move mechanics won't kick in and they will be simply copied.
Operators are functions and there are no exceptions in this case. Special move operator hasn't been proposed and is extremly unlikely to be because it would make the standard longer and more complex (compilers would also be heavily affected) for a feature that saves a couple chars.
Universal references as parameter or return type
I read a few articles about universal references but I still don't understand in which cases I might need to use this as a parameter type besides the move constructor. Could someone enlighten me?
void Foo(Bar&& x);
Bar&& Foo();
Under which circumstances would I ever want to have this which I couldn't solve with a simple Bar& to move something?
When to use std::move
Could someone explain me when an explicit std::move is necessary (for parameters and return types) under which circumstances I can expect that the compiler uses it automatically during the optimization phase? For example
struct A { A(A&& src) ... };
A Foo()
{
A a;
...
return a;
}
In this case I might benefit from RVO, so should I even ever consider using std::move for a result? Thanks a lot!
Universal references
The example you've provided doesn't actually use universal references, those are just r-value references. Syntactically, the universal reference is an r-value reference to a parameter of a deduce templated type:
template <typename Bar>
void foo(Bar &&bar);
This is actually different then a regular r-value reference and it is used to solve a perfect forwarding problem. But I assume this isn't what your question is about.
R-value references
In most cases when you want to move the value to or from the function you can simply do it by value:
void foo(Bar b);
...
Bar somebar;
foo(std::move(somebar)); //function argument is move-constructed
/**************************************************************/
Bar foo()
{
Bar somebar;
return somebar; //return value is move-constructed
}
Doing this using l-value reference is actually incorrect:
void foo(Bar &b)
{
Bar somebar = std::move(b); //you "stole" passed value
}
...
Bar somebar;
foo(somebar); //but the caller didn't intend to move his value
Also returning any reference to a local variable is wrong.
The only reason one would use r-value reference instead of passing by value is to allow moving the value without actually moving it one extra time:
Bar &&Foo::foo()
{
return memberBar;
}
...
Foo f;
Bar b = f.foo(); //"b" will be move-constructed
...
f.foo().doBar(); //returned "Bar" value is just used and not moved at all
When to use std::move
You need to use std::move every time you want to move a variable even if it's already an r-value reference:
Foo::Foo(Bar &&bar)
: memberBar(std::move(bar)) //still need to move explicitly!
{
}
You don't need to use std::move when:
Returning a local variable by value
Passing a temporary to a function, e.g. foo(Bar())
Passing non-movable types (those without move-constructor) including primitive types
A common mistake:
Bar *bar = new Bar();
foo(std::move(bar)); //not needed! nothing to move since the pointer is passed and not the object itself
However when using a conditional operator:
Bar foo()
{
Bar somebar;
Bar otherbar;
return std::move(true ? somebar : otherbar); //need to move explicitly!
}
I've written a class similar to the following:
class ScriptThread {
public:
ScriptThread(): mParent() {}
private:
ScriptThread(ScriptThread *parent): mParent(parent) {}
public:
ScriptThread(ScriptThread &&rhs);
ScriptThread &operator = (ScriptThread &&rhs);
// copy constructor/assignment deleted implicitly
ScriptThread &execute(const Script &script);
ScriptThread spawn();
ScriptThread spawn(const Script &script);
private:
ScriptThread *mParent;
};
ScriptThread &ScriptThread::execute(const Script &script) {
// start executing the given script
return *this;
}
ScriptThread ScriptThread::spawn() {
// create a ScriptThread with "this" as its parent
return ScriptThread(this);
}
ScriptThread ScriptThread::spawn(const Script &script) {
// convenience method to spawn and execute at the same time
return spawn().execute(script); // ERROR: "use of deleted function"
}
As written, g++ fails to compile it at the line marked "ERROR", claiming that it's trying to use the (deleted) copy constructor. However, if I replace the last function with this:
ScriptThread ScriptThread::spawn(const Script &script) {
ScriptThread thread = spawn();
thread.execute(script);
return thread;
}
It compiles without an error. Even after referring to a number of articles, references, and other SO questions, I don't understand: why does the first invoke the copy constructor at all? Isn't the move constructor enough?
execute(script) returns an lvalue. You can't implicitly move from an lvalue, so to use the move constructor for the returned object you would need to say
return std::move(spawn().execute(script));
You didn't do this so it tries to use the copy constructor, because that's how you make new objects from lvalues.
In your replacement case you have:
return thread;
Here thread is also an lvalue, but it's about to go out of scope as soon as the function ends, so conceptually it can be considered to be like a temporary or other variable that is going to disappear at the end of the expression. Because of this there is a special rule in the C++ standard that says the compiler treats such local variables as rvalues, allowing the move constructor to be used even though thread is really an lvalue.
See Barry's more complete answer for the references to the standard where the special rule is defined, and the full details of the rule.
ScriptThread is noncopyable (the implicit copy constructor/assignment operators are defined as deleted because you declared move constructor/assignment). In spawn(), your original implementation:
ScriptThread ScriptThread::spawn(const Script &script) {
return spawn().execute(script);
}
is attempting to construct a ScriptThread from an lvalue reference (execute returns a ScriptThread&). That will call the copy constructor, which is deleted, hence the error.
However, in your second attempt:
ScriptThread ScriptThread::spawn(const Script &script) {
ScriptThread thread = spawn();
thread.execute(script);
return thread;
}
we run into the rule, from [class.copy]:
When the criteria for elision of a copy/move operation are met, but not for an exception-declaration, and the
object to be copied is designated by an lvalue, or when the expression in a return statement is a (possibly
parenthesized) id-expression that names an object with automatic storage duration declared in the body or
parameter-declaration-clause of the innermost enclosing function or lambda-expression, overload resolution
to select the constructor for the copy is first performed as if the object were designated by an rvalue.
Even though thread is an lvalue, we perform overload resolution on the constructor of ScriptThread as if it were an rvalue. And we do have a valid constructor for this case: your move constructor/assignment.
That's why the replacement is valid (and uses move construction), but the original failed to compile (because it required copy construction).
For below code, I want to use the std::move to improve the efficiency. I have two functions, the first function uses std::move, and the second function just calls the first function. So, do I need to use std::move again in the function "vector convertToString()"? Why and why not? Thank you.
class Entity_PortBreakMeasure
{
public:
Entity_PortBreakMeasure(){}
int portfolioId;
string portfolioName;
int businessDate;
string assetType;
string currency;
string country;
string industry;
string indicator;
double value;
inline double operator()()
{
return value;
}
static vector<string> convertToString(Entity_PortBreakMeasure& pbm)
{
//PORTFOLIOID INDUSTRY CURRENCY COUNTRY BUSINESSDATE ASSETTYPE INDICATOR VALUE PORTFOLIONAME
vector<string> result;
result.push_back(boost::lexical_cast<string>(pbm.portfolioId));
result.push_back(pbm.industry);
result.push_back(pbm.currency);
result.push_back(pbm.country);
result.push_back(Date(pbm.businessDate).ToString());
result.push_back(pbm.assetType);
result.push_back(pbm.indicator);
result.push_back(boost::lexical_cast<string>(pbm.value));
result.push_back(pbm.portfolioName);
return std::move(result);
}
vector<string> convertToString()
{
return convertToString(*this);
}
move() shouldn't be used for either of these functions.
In the first function, you're returning a local variable. Without move(), most (all?) compilers will perform NRVO and you won't get a copy or a move -- the returned variable will be constructed directly in the returned value for the caller. Even if the compiler is, for some reason, unable to do NRVO, local variables become r-values when used as the argument to a return, so you'll get a move anyway. Using move() here serves only to inhibit NRVO and force the compiler to do a move (or a copy in the event that the move isn't viable).
In the second function, you're returning an r-value already, since the first function returns by value. move() here doesn't add anything but complexity (which might possibly confuse an optimizer into producing suboptimal code or failing to do copy elision).