I'd like to move unique_ptr to its object's method:
class Foo {
void method(std::unique_ptr<Foo>&& self) {
// this method now owns self
}
}
auto foo_p = std::make_unique<Foo>();
foo_p->method(std::move(foo_p));
This compiles, but I don't know if it is not Undefined behavior. Since I moved from the object when also calling a method on it.
Is it UB?
If it is, I could probably fix it with:
auto raw_foo_p = foo_p.get();
raw_foo_p->method(std::move(foo_p))
right?
(Optional Motivation:)
Pass the object around to extend its lifetime. It would live in a lambda until the lambda would be called asynchronously. (boost::asio)
Please, see Server::accept first and then Session::start.
You can see the original implementation used shared_ptr, but I don't see why would that be justified, since I only need one owner of my Session object.
Shared_ptr makes code more complex and it was hard for me to understand, when not familiar with shared_ptr.
#include <iostream>
#include <memory>
#include <utility>
#include <boost/asio.hpp>
using namespace boost::system;
using namespace boost::asio;
using boost::asio::ip::tcp;
class Session /*: public std::enable_shared_from_this<Session>*/ {
public:
Session(tcp::socket socket);
void start(std::unique_ptr<Session>&& self);
private:
tcp::socket socket_;
std::string data_;
};
Session::Session(tcp::socket socket) : socket_(std::move(socket))
{}
void Session::start(std::unique_ptr<Session>&& self)
{
// original code, replaced with unique_ptr
// auto self = shared_from_this();
socket_.async_read_some(buffer(data_), [this/*, self*/, self(std::move(self))] (error_code errorCode, size_t) mutable {
if (!errorCode) {
std::cout << "received: " << data_ << std::endl;
start(std::move(self));
}
// if error code, this object gets automatically deleted as `self` enters end of the block
});
}
class Server {
public:
Server(io_context& context);
private:
tcp::acceptor acceptor_;
void accept();
};
Server::Server(io_context& context) : acceptor_(context, tcp::endpoint(tcp::v4(), 8888))
{
accept();
}
void Server::accept()
{
acceptor_.async_accept([this](error_code errorCode, tcp::socket socket) {
if (!errorCode) {
// original code, replaced with unique_ptr
// std::make_shared<Session>(std::move(socket))->start();
auto session_ptr = std::make_unique<Session>(std::move(socket));
session_ptr->start(std::move(session_ptr));
}
accept();
});
}
int main()
{
boost::asio::io_context context;
Server server(context);
context.run();
return 0;
}
compiles with:
g++ main.cpp -std=c++17 -lpthread -lboost_system
For your first code block:
std::unique_ptr<Foo>&& self is a reference and assigning it an argument std::move(foo_p), where foo_p is a named std::unique_ptr<Foo> will only bind the reference self to foo_p, meaning that self will refer to foo_p in the calling scope.
It does not create any new std::unique_ptr<Foo> to which the ownership of the managed Foo object may be transferred. No move construction or assignment happens and the Foo object is still destroyed with the destruction of foo_p in the calling scope.
Therefore there is no risk of undefined behavior in this function call itself, although you could use the reference self in a way that could cause undefined behavior in the body.
Maybe you intended to have self be a std::unique_ptr<Foo> instead of std::unique_ptr<Foo>&&. In that case self would not be a reference, but an actual object to which ownership of the managed Foo would be transferred via move construction if called with std::move(p_foo) and which would be destroyed after the function call in foo_p->method(std::move(foo_p)) together with the managed Foo.
Whether this alternative variant is in itself potentially undefined behavior depends on the C++ standard version in use.
Before C++17 the compiler was allowed to choose to evaluate the call's arguments (and the associated move construction of the parameter) before evaluating foo_p->method. This would mean, that foo_p could have already moved from when foo_p->method is evaluated, causing undefined behavior. This could be fixed similarly to how you propose to do it.
Since C++17 it is guaranteed that the postfix-expression (here foo_p->method) is evaluated before any of the arguments of the call are and therefore the call itself would not be a problem. (Still the body could cause other issues.)
In detail for the latter case:
foo_p->method is interpreted as (foo_p->operator->())->method, because std::unique_ptr offers this operator->(). (foo_p->operator->()) will resolve to a pointer to the Foo object managed by the std::unique_ptr. The last ->method resolves to a member function method of that object. In C++17 this evaluation happens before any evaluation of the arguments to method and is therefore valid, because no move from foo_p has happened yet.
Then the evaluation order of the arguments is by design unspecified. So probably A) the unique_ptr foo_p could get moved from before this as an argument would be initialized. And B) it will get moved from by the time method runs and uses the initialized this.
But A) is not a problem, since § 8.2.2:4, as expected:
If the function is a non-static member function, the this parameter of the function shall be initialized with a pointer to the object of the call,
(And we know this object was resolved before any argument was evaluated.)
And B) won't matter as: (another question)
the C++11 specification guarantees that transferring ownership of an object from one unique_ptr to another unique_ptr does not change the location of the object itself
For your second block:
self(std::move(self)) creates a lambda capture of type std::unique_ptr<Session> (not a reference) initialized with the reference self, which is referring to session_ptr in the lambda in accept. Via move-construction ownership of the Session object is transferred from session_ptr to the lambda's member.
The lambda is then passed to async_read_some, which will (because the lambda is not passed as non-const lvalue reference) move the lambda into internal storage, so that it can be called asynchronously later. With this move, the ownership of the Session object transfers to the boost::asio internals as well.
async_read_some returns immediately and so all local variables of start and the lambda in accept are destroyed. However the ownership of Session was already transferred and so there is no undefined behavior because of lifetime issues here.
Asynchronously the lambda's copy will be called, which may again call start, in which case the ownership of Session will be transferred to another lambda's member and the lambda with the Session ownership will again be moved to internal boost::asio storage. After the asynchronous call of the lambda, it will be destroyed by boost::asio. However at this point, again, ownership has already transferred.
The Session object is finally destroyed, when if(!errorCode) fails and the lambda with the owning std::unique_ptr<Session> is destroyed by boost::asio after its call.
Therefore I see no problem with this approach with regards to undefined behavior relating to Session's lifetime. If you are using C++17 then it would also be fine to drop the && in the std::unique_ptr<Session>&& self parameter.
Related
This is a simplified version of what I am doing:
#include <iostream>
#include <functional>
class thing
{
public:
void register_fn(const std::function<void()> &fn)
{
m_fn = fn;
}
void run()
{
m_fn();
}
std::function<void()> m_fn;
};
int main() {
// Create a thing object
thing t;
// In a limited scope
{
// Local lambda
auto afn = []{std::cout << "hi\n";};
// Store the lamda by reference
t.register_fn(afn);
}
// Run the stored lambda (which should be destroyed - therefore dangling reference??)
t.run();
// Take a copy
thing t2 = t;
t2.run();
return 0;
}
see it running here: https://godbolt.org/z/6qW3ro
So, I have a class that stores a temporary lambda passed by reference. The lamda afn's scope is limited so that once it is passed to the register function it goes out of scope. Then outside of this scope I call the run function which should be running the (dangling?) reference to the lambda.
This has been working, but recently looking back at my code I have a doubt. Since the lambda is temporary (done here by limiting the scope of my lambda object afn) - this should not work... I can't quite get my head around why it is working - unless by luck and this is undefined behaviour?
Or... what have I mis-understood here? - because that is probably the most likely explanation!
This works principally because you make a copy of the function object. In
void register_fn(const std::function<void()> &fn)
{
m_fn = fn;
}
you assign fn to m_fn which makes a copy and and even though fn is a reference to the local lambda, making a copy means m_fn does not refer to fn, but will get a copy of the function fn has stored in it. This means there is no dangling reference and your code has well defined behavior.
This would be different if the lambda captured a local object by reference, as that capture would become invalid after you leave the scope where the lambda was declared.
If I create a lambda in a function and capture a variable to the lambda using std::move, when does the move happen? Is it when the lambda is created or when the lambda is executed?
Take the following code for example ... when do the various moves happen? Is it thread safe if myFunction is called on one thread and testLambda is executed on another thread?
class MyClass {
private:
// Only accessed on thread B
std::vector<int> myStuff;
// Called from thread A with new data
void myFunction(const std::vector<int>&& theirStuff) {
// Stored to be called on thread B
auto testLambda = [this, _theirStuff{ std::move(theirStuff) }]() {
myStuff = std::move(_theirStuff);
};
// ... store lambda
}
// Elsewhere on thread A
void someOtherFunction() {
std::vector<int> newStuff = { 1, 2, .... n };
gGlobalMyClass->myFunction(std::move(newStuff));
}
If I create a lambda in a function and capture a variable to the lambda using std::move, when does the move happen? Is it when the lambda is created or when the lambda is executed?
If you had written what I believe you intended to write, then the answer would be: both. Currently, the answer is: neither. You have a lambda capture _theirStuff { std::move(theirStuff) }. This basically declares a member of the closure type, which will be initialized when the closure object is created as if it were
auto _theirStuff { std::move(theirStuff) };
You also have
myStuff = std::move(_theirStuff);
in the lambda body.
However, your parameter theirStuff is actually an rvalue reference to a const std::vector<int>. Thus, _theirStuff { std::move(theirStuff) } is not actually going to perform a move, because a const std::vector cannot be moved from. Most likely, you wanted to write std::vector<int>&& theirStuff instead. Furthermore, as pointed out by #JVApen in the comments below, your lambda is not mutable. Therefore, _theirStuff will actually be const as well, and, thus, also cannot be moved from. Consequently, your code above, despite all the std::move, will actually make a copy of the vector every time. If you had written
void myFunction(std::vector<int>&& theirStuff)
{
auto testLambda = [this, _theirStuff { std::move(theirStuff) }]() {
myStuff = std::move(_theirStuff);
};
}
You would be moving theirStuff into _theirStuff when the closure object is created. And you would be copying _theirStuff into myStuff when the lambda is called. If you had written
void myFunction(std::vector<int>&& theirStuff)
{
auto testLambda = [this, _theirStuff { std::move(theirStuff) }]() mutable {
myStuff = std::move(_theirStuff);
};
}
Then you would be moving theirStuff into _theirStuff when the closure object is created. And you would be moving _theirStuff into myStuff when the lambda is called. Note that, as a consequence, your lambda then cannot really be called twice. I mean, it can, but it will only really work once since _theirStuff will be empty after the first time the lambda is called…
Also, note that above description is only valid for the particular combination of types in your example. There is no general definition of what it actually means to move an object. What it means to move an object is entirely up to the particular type of the object. It may not even mean anything. std::move itself does not really do anything. All it does is cast the given expression to an rvalue reference. If you then initialize another object from the result of std::move, or assign the result to an object, overload resolution will pick a move constructor or move assignment operator—if one exists—instead of the normal copy constructor or copy assignment operator. It is then up to the implementation of the move constructor/move assignment operator of the respective type to actually perform a move, i.e., do whatever it is that's supposed to be done for the particular type in case of initialization or assignment from an rvalue. So, in a way, what you do when you apply std::move is that you advertise the respective object as "this may be moved from". Whether or not it actually will be moved from (and, if so, what that actually means) is up to the implementation. In the particular case of std::vector, the move constructor/move assignment operator, by definition, guarantee that not only the contents of the original vector will be taken over from the original object, but also that the original object will be empty afterwards. In many other cases, it may be undefined behavior to do anything with an object that was moved from (except, maybe, destroy it; that one can be pretty much taken for granted as a type that doesn't at least allow that would be pretty much useless; typically, you will at least be able to assign a new value to an object that was moved from, but even that is not guaranteed in general). You always have to check for the particular type at hand what condition an object is guaranteed to be in after having been moved from…
I am reading "C++ Concurrency in Action".
I have following doubt with the below code snippet.
struct func
{
int& i;
func(int& i_):i(i_){}
void operator()()
{
for(unsigned j=0;j<1000000;++j)
{
do_something(i); //Can have issue of dangling references
}
}
};
void oops()
{
int some_local_state=0;
func my_func(some_local_state);
std::thread my_thread(my_func);
my_thread.detach();
}
The author says in order to avoid such scenario one way is to
make the thread function self-contained and copy the data into the thread rather than sharing the data
I do understand the problem is because of the fact the function object created is local to the oop function and when the oops function finishes the object goes out of scope, but I cannot understand how to avoid in way author has mentioned it.
The issue is not the func object. std::thread will copy your functor.
First the constructor copies/moves all arguments (both the function
object f and all args...) to thread-accessible storage
The issue is the reference int& i; your functor keeps to some_local_state which indeed will be invalid once some_local_state runs out of scope.
To solve this copy the value of some_local_state instead of keeping a reference to it. If you need shared access, consider using a std::shared_ptr.
What is the reason that you can't move an object to another std::thread? There are situations where it could be useful. For example:
You create a loop that accepts incoming socket connections. It would be nice to move incoming connections to another thread that will handle the connections. You don't need the connection anymore in the accept loop. So why should you create a pointer?
A small test case:
#include <iostream>
#include <thread>
using namespace std;
class Pointertest
{
public:
Pointertest() {cout << "Constructor";}
Pointertest(Pointertest &pointertest) {cout << "Copy";}
Pointertest(Pointertest &&pointertest) {cout << "Move";}
~Pointertest() {cout << "Destruct";}
};
void foo(Pointertest &&pointertest)
{
}
int main()
{
Pointertest pointertest;
foo(std::move(pointertest)); //Works
thread test(foo,std::move(pointertest)); //**cannot convert parameter 1 from 'Pointertest' to 'Pointertest &&'**
}
The std::thread constructor has to treat the arguments you give it somewhat differently from most forwarding functions.
The reason for this is due to questions of when the thread actually gets started. If the part of the function invocation that actually created the function argument gets run long after the thread object is created (which is entirely legal behavior), then the object that needs to be moved from may have long since been destroyed.
Just consider an altered version of your code:
std::thread some_func()
{
Pointertest pointertest;
thread test(foo,std::move(pointertest));
return test;
}
This is perfectly valid (the thread will be moved out of the function). However, there's a big problem. foo may not have been called yet. And since foo takes its parameter by reference, it now has a reference to a stack variable that has been destroyed.
That's bad. But even if foo took its parameter by value, it would change nothing. Because the actual movement into that parameter doesn't happen until some indeterminate time after the thread has been started. The attempt to move into the parameter will still use an rvalue reference to a stack variable that has been destroyed. Which again is bad.
Therefore, std::thread constructor does something different. It copy/moves the arguments you give it into internal storage (this is done on the current thread). Then it uses those values as arguments for the actual function call (this is done on the new thread).
According to the standard, the thread constructor should treat pass these internal variables to your functions as temporaries. The standard specifically states INVOKE (DECAY_COPY ( std::forward<F>(f)), DECAY_COPY (std::forward<Args>(args))...), where the DECAY_COPY stuff happens on the original thread, while the INVOKE part happens on the new thread.
So it seems like your thread implementation isn't able to forward the non-copyable parameters through correctly. You should be able to pass a non-copyable type; the arguments are only required to be MoveConstructible.
So this would appear to be a bug in your implementation.
It is possible. Fixing the signature of your copy constructor makes it work for me:
class Pointertest
{
public:
Pointertest() {cout << "Constructor";}
Pointertest(Pointertest const& pointertest) {cout << "Copy";}
// ^^^^^^
Pointertest(Pointertest &&pointertest) {cout << "Move";}
~Pointertest() {cout << "Destruct";}
};
Also, do not forget to join your thread (or detach from it) before your thread object goes out of scope:
int main()
{
Pointertest pointertest;
thread test(foo, std::move(pointertest));
test.join();
// ^^^^^^^^^^^^
}
is it possible to restrict class instances to be used only as rvalues (e.g. temporaries)?
for example, I have class Wrapper whose constructor takes A const& and saves this reference in its member. It's a dangerous because lifetime of Wrapper instance cannot be longer than lifetime of A instance, but it's fine if Wrapper is temporary.
I think that even wanting to do this is a sign of a really bad design.
However, you could make all constructors private and make a friend function that returns an rvalue. That should do the trick.
I don't think it would be safe:
const A &a = YourClass( tmp );
YourClass in this case is the class you're looking for which only allow temporary instances, tmp is the temporary value you pass to the constructor.
It's possible (ie: safe, defined behavior) to have a constant reference to a temporary (ie: a), but the temporary itself (such instance of YourClass) has got a reference to tmp which is no longer valid after that expression is evaluated.
Not exactly the answer you are looking for, but have you thought about weak pointers? (for example, boost::weak_ptr). In this case, the original A would be held in a shared_ptr and the Wrapper constructor accepts a weak_ptr. The neat thing with this approach is that, before each usage of the weak_ptr, you can attempt to lock() which will give you a shared_ptr - if that fails, you know that A is gone and Wrapper cannot function... But it's handled cleanly...
This might do the job unless your class has public data members.
Basically, the idea is not to restrict the construction of the wrapper but to make sure that instances can be used (just like you said) only as long as they are temporary values. One can achieve this by overloading all methods and deleting (or making them private) those that refer to const&.
Here's a simple example:
class Wrapper
{
public:
Wrapper() = default;
Wrapper(const std::string& name) : name(name) {}
void process() && { std::cout << "Greetings from " << name << std::endl; }
// Only temporary instances of this class are allowed!
void process() const & = delete;
private:
std::string name;
};
And some use cases:
Wrapper("John").process(); // intended use case
Wrapper j; // create whatever you want
j.process(); // error C2280: 'void Wrapper::process(void) const &': attempting to reference a deleted function
std::move(j).process(); // this is still possible
const Wrapper& t = Wrapper(); // bind the temporary to a const reference - not a problem because ...
t.process(); // error C2280: 'void Wrapper::process(void) const &': attempting to reference a deleted function
The obvious disadvantages are:
You have to overload every public member function.
The error message is delayed and not very informative.
A similar thing has been done in the standard. The make routines for std::reference_wrapper do not accept temporaries.
Note that they considered another subtlety: the overload uses const T&& instead of T&&. This can be important in our case as well. For example, if your wrapper is deliberately designed to be noncopyable and you use make routines such as
const Wrapper make_wrapper();
instead of
Wrapper make_wrapper();
In this case, you might want to replace
void process() &&;
by
void process() const &&;
I'd not bother enforcing this at compile time, as there are always going to be corner cases where this would be overly restrictive, limiting the usefulness of the class, but rather wrap tools like valgrind or Purify so I can spot places where invalidated references are used.
I believe in C++17 and later you can get approximately what you want by doing the following:
Delete the move constructor for your type (and don't define a copy constructor).
Always accept your type by value in APIs.
So, for example:
#include <type_traits>
#include <utility>
// A non-moveable, non-copyable type.
struct CantMove {
CantMove(CantMove&&) = delete;
CantMove(int) {} // Some other constructor
};
static_assert(!std::is_move_constructible_v<CantMove>);
static_assert(!std::is_copy_constructible_v<CantMove>);
// A function that accepts it by value.
bool AcceptByValue(CantMove input) { return true; }
// It's possible to call the value-accepting API when the input is a prvalue
// (which in previous versions of C++ would have been a temporary).
bool unused = AcceptByValue(CantMove(0));
// But it's not possible to call with a named value, even when casted to an
// rvalue reference. This doesn't compile.
CantMove cant_move(0);
bool unused_2 = AcceptByValue(std::move(cant_move));
It's possible to provide the value-accepting function with what we previously called a temporary because guaranteed copy elision says that there isn't even a temporary involved anymore—the only CantMove object created is the function parameter itself, so there is no move- or copy-construction involved. In contrast it's not possible to call with std::move(cant_move) because that would involve move-constructing the function parameter, and the type is not move-constructible.
Of course it's still possible to initialize a CantMove directly:
CantMove foo{0};
But if you own all of the APIs that accept a CantMove and make them all accept by value, then you can't actually do anything with foo afterward. This means it would be hard for a user to do this by mistake and not realize the problem.
Yes, you could.
You would make the constructor and regular copy-constructor/assign private but make the r-value move semantics (C++0x) public.
You would have a static or friend constructor to create the temporary.
In 2003 C++ you would also be able to use this to bind to a const reference.
Of course you'd have the issue that your const reference would probably become invalidated after the statement.