Can somebody explain the behavior of the following code?
When I explicitely convert my lambda to an std::function, the lambda correctly captures my variable n.
When it is implicitly converted to an std::function (using a temporary), then the capture fails.
I am using g++-4.9 (Ubuntu 4.9-20140406-1ubuntu1) 4.9.0 20140405 (experimental) [trunk revision 209157]
#include <chrono>
#include <iostream>
#include <memory>
#include <thread>
std::shared_ptr<std::thread> call(const std::function<void()>& functor)
{
// Execute our functor asynchronously
return std::make_shared<std::thread>([&functor]
{
// Make sure all temporary are deallocated
std::this_thread::sleep_for(std::chrono::seconds(1));
// Execute our functor
functor();
});
}
int main()
{
int n{};
std::cout << "in main " << &n << std::endl;
// -> in main 0x7fffd4e1a96c
auto lambda = [&n]
{
std::cout << "in lambda " << &n << std::endl;
};
// Here we do an explicit convertion to std::function
std::cout << "explicit convertion" << std::endl;
auto function = std::function<void()>{ lambda };
auto pThreadFunction = call(function);
pThreadFunction->join();
// -> in lambda 0x7fffd4e1a96c
// Here we use an implicit convertion to std::function
std::cout << "implicit convertion" << std::endl;
auto pThreadLambda = call(lambda);
pThreadLambda->join();
// -> in lambda 0
return 0;
}
The lifetime of a temporary constructed for binding to a const reference function parameter is the full-expression containing that function call, so your thread function is referring to a dangling reference.
You should only capture variables into a thread function by reference if you can guarantee that the lifetime of the variable contains the lifetime of the thread, as you have done in the case where function is a local variable in main.
One alternative would be to call join within the full-expression that constructs the temporary:
call(lambda)->join();
Another more general solution would be to capture functor by value in your thread function.
Related
I am new to boost:asio. I need to pass shared_ptr as argument to handler function.
E.g.
boost::asio::post(std::bind(&::function_x, std::move(some_shared_ptr)));
Is using std::move(some_shared_ptr) correct? or should I use as below,
boost::asio::post(std::bind(&::function_x, some_shared_ptr));
If both are correct, which one is advisable?
Thanks in advance
Regards
Shankar
Bind stores arguments by value.
So both are correct and probably equivalent. Moving the argument into the bind is potentially more efficient if some_argument is not gonna be used after the bind.
Warning: Advanced Use Cases
(just skip this if you want)
Not what you asked: what if function_x took rvalue-reference arguments?
Glad you asked. You can't. However, you can still receive by lvalue reference and just move from that. because:
std::move doesn't move
The rvalue-reference is only there to indicate potentially-moved-from arguments enabling some smart compiler optimizations and diagnostics.
So, as long as you know your bound function is only executed once (!!) then it's safe to move from lvalue parameters.
In the case of shared-pointers there's actually a little bit more leeway, because moving from the shared-ptr doesn't actually move the pointed-to element at all.
So, a little exercise demonstrating it all:
Live On Coliru
#include <boost/asio.hpp>
#include <memory>
#include <iostream>
static void foo(std::shared_ptr<int>& move_me) {
if (!move_me) {
std::cout << "already moved!\n";
} else {
std::cout << "argument: " << *std::move(move_me) << "\n";
move_me.reset();
}
}
int main() {
std::shared_ptr<int> arg = std::make_shared<int>(42);
std::weak_ptr<int> observer = std::weak_ptr(arg);
assert(observer.use_count() == 1);
auto f = std::bind(foo, std::move(arg));
assert(!arg); // moved
assert(observer.use_count() == 1); // so still 1 usage
{
boost::asio::io_context ctx;
post(ctx, f);
ctx.run();
}
assert(observer.use_count() == 1); // so still 1 usage
f(); // still has the shared arg
// but now the last copy was moved from, so it's gone
assert(observer.use_count() == 0); //
f(); // already moved!
}
Prints
argument: 42
argument: 42
already moved!
Why Bother?
Why would you care about the above? Well, since in Asio you have a lot of handlers that are guaranteed to execute precisely ONCE, you can sometimes avoid the overhead of shared pointers (the synchronization, the allocation of the control block, the type erasure of the deleter).
That is, you can use move-only handlers using std::unique_ptr<>:
Live On Coliru
#include <boost/asio.hpp>
#include <memory>
#include <iostream>
static void foo(std::unique_ptr<int>& move_me) {
if (!move_me) {
std::cout << "already moved!\n";
} else {
std::cout << "argument: " << *std::move(move_me) << "\n";
move_me.reset();
}
}
int main() {
auto arg = std::make_unique<int>(42);
auto f = std::bind(foo, std::move(arg)); // this handler is now move-only
assert(!arg); // moved
{
boost::asio::io_context ctx;
post(
ctx,
std::move(f)); // move-only, so move the entire bind (including arg)
ctx.run();
}
f(); // already executed
}
Prints
argument: 42
already moved!
This is going to help a lot in code that uses a lot of composed operations: you can now bind the state of the operation into the handler with zero overhead, even if it's bigger and dynamically allocated.
I'm having troubles understanding fully the assignment operator for unique_ptr. I understand that we can only move them, due to the fact that copy constructor and assignment operators are deleted, but what if
a unique_ptr which contains already an allocation is overwritten by a move operation? Is the content previously stored in the smart pointer free'd?
#include <iostream>
#include <memory>
class A{
public:
A() = default;
virtual void act() const {
std::cout << "act from A" << std::endl;
}
virtual ~A() {
std::cout << "destroyed A" << std::endl;
}
};
class B : public A {
public:
B() : A{} {}
void act() const override {
std::cout << "act from B" << std::endl;
}
~B() override {
std::cout << "destroyed from B " << std::endl;
}
};
int main() {
auto pP{std::make_unique<A>()};
pP->act();
==================== ! =======================
pP = std::make_unique<B>(); // || std::move(std::make_unique<B>())
==================== ! =======================
pP->act();
return 0;
}
When I do
pP = std::make_unique<B>();
does it mean that what was allocated in the first lines for pP (new A()) is destructed automatically?
Or should I opt for:
pP.reset();
pP = std::make_unique<B>();
Yes, see section 20.9.1, paragraph 4 of the C++11 draft standard
Additionally, u can, upon request, transfer ownership to another unique pointer u2. Upon completion of
such a transfer, the following postconditions hold:
u2.p is equal to the pre-transfer u.p,
u.p is equal to nullptr, and
if the pre-transfer u.d maintained state, such state has been transferred to u2.d.
As in the case of a reset, u2 must properly dispose of its pre-transfer owned object via the pre-transfer
associated deleter before the ownership transfer is considered complete
In other words, it's cleaning up after itself upon assignment like you'd expect.
Yes, replacing the content of a smart pointer will release the previously-held resource. You do not need to call reset() explicitly (nor would anyone expect you to).
Just for the sake of this particular example. It seems polymorphism in your example didn't allow you to draw clear conclusions from output:
act from A
destroyed A
act from B
destroyed from B
destroyed A
So let's simplify your example and make it straight to the point:
#include <iostream>
#include <memory>
struct A {
explicit A(int id): id_(id)
{}
~A()
{
std::cout << "destroyed " << id_ << std::endl;
}
int id_;
};
int main() {
std::unique_ptr<A> pP{std::make_unique<A>(1)};
pP = std::make_unique<A>(2);
}
which outputs:
destroyed 1
destroyed 2
Online
I hope this leaves no room for misinterpretation.
As I understand it, one cannot change the reference variable once it has been initialized. See, for instance, this question. However, here is a minmal working example which sort of does reassign it. What am I misunderstanding? Why does the example print both 42 and 43?
#include <iostream>
class T {
int x;
public:
T(int xx) : x(xx) {}
friend std::ostream &operator<<(std::ostream &dst, T &t) {
dst << t.x;
return dst;
}
};
int main() {
auto t = T(42);
auto q = T(43);
auto &ref = t;
std::cerr << ref << std::endl;
ref = q;
std::cerr << ref << std::endl;
return 0;
}
You're not changing the reference here.
You are replacing the object the reference is referring to.
In other words: after the assignment, your t is replaced by q.
ref is still a reference to t.
That does not perform a reference reassignment. Instead, it copy assigns the object in variable q into the object referenced by ref (which is t in your example).
This also justifies why you got 42 as output: the default copy assignment operator modified the first object.
Given a class A with two constructors, taking initializer_list<int> and initializer_list<initializer_list<int>> respectively, then
A v{5,6};
calls the former, and
A v{{5,6}};
calls the latter, as expected. (clang3.3, apparently gcc behaves differently, see the answers. What does the standard require?)
But if I remove the second constructor, then A v{{5,6}}; still compiles and it uses the first constructor. I didn't expect this.
I thought that A v{5,6} would be the only way to access the initializer_list<int> constructor.
(I discovered this while playing around with std::vector and this question I asked on Reddit, but I created my own class A to be sure that it wasn't just a quirk of the interface for std::vector.)
I think this answer might be relevant.
Yes, this behaviour is intended, according to §13.3.1.7 Initialization
by list-initialization
When objects of non-aggregate class type T are list-initialized (8.5.4), overload resolution selects the constructor in two phases:
— Initially, the candidate functions are the initializer-list constructors (8.5.4) of the class T and the argument list consists of
the initializer list as a single argument.
— If no viable initializer-list constructor is found, overload resolution is performed again, where the candidate functions are all
the constructors of the class T and the argument list consists of the
elements of the initializer list.
In gcc I tried your example. I get this error:
error: call of overloaded 'A(<brace-enclosed initializer list>)' is ambiguous
gcc stops complaining if I use three sets of brace. i.e.:
#include <iostream>
#include <vector>
#include <initializer_list>
struct A {
A (std::initializer_list<int> il) {
std::cout << "First." << std::endl;
}
A (std::initializer_list<std::initializer_list<int>> il) {
std::cout << "Second." << std::endl;
}
};
int main()
{
A a{0}; // first
A a{{0}}; // compile error
A a2{{{0}}}; // second
A a3{{{{0}}}}; // second
}
In an attempt to mirror the vector's constructors, here are my results:
#include <iostream>
#include <vector>
#include <initializer_list>
struct A {
A (std::initializer_list<int> il) {
std::cout << "First." << std::endl;
}
explicit A (std::size_t n) {
std::cout << "Second." << std::endl;
}
A (std::size_t n, const int& val) {
std::cout << "Third." << std::endl;
}
A (const A& x) {
std::cout << "Fourth." << std::endl;
}
};
int main()
{
A a{0};
A a2{{0}};
A a3{1,2,3,4};
A a4{{1,2,3,4}};
A a5({1,2,3,4});
A a6(0);
A a7(0, 1);
A a8{0, 1};
}
main.cpp:23:10: warning: braces around scalar initializer
A a2{{0}};
^~~
1 warning generated.
First.
First.
First.
First.
First.
Second.
Third.
First.
I want to pass out some parameter from the lambda function, so I bind a reference parameter to a lambda function. However, the outer variable is not changed after calling the function. If I bind the lambda function with the pointer of outer variable, the result is correct.
I show the test program as follows and want to know why the outer variable does not change, given that I have already defined the pass-by-reference lambda function [&]?
#include <iostream>
#include <vector>
#include <algorithm>
#include <functional>
using namespace std;
int main(int argc, char* argv[])
{
string acc_ref, acc_ptr;
// define function via reference
auto fProcRefEx = [&](string& acc, const string& s) -> void
{
acc += s;
};
auto fProcRef = bind(fProcRefEx, acc_ref, placeholders::_1);
// define function via pointer
auto fProcPtrEx = [&](string* pacc, const string& s) -> void
{
(*pacc) += s;
};
auto fProcPtr = bind(fProcPtrEx, &acc_ptr, placeholders::_1);
// test
vector<string> v = {"abc", "def"};
for_each(v.begin(), v.end(), fProcRef);
cout << "acc_ref: " << acc_ref << endl; // acc_ref is empty, wrong
for_each(v.begin(), v.end(), fProcPtr);
cout << "acc_ptr: " << acc_ptr << endl; // acc_ptr is "abcdef", correct
return 0;
}
I think std::bind will decay the reference to just a plain value type when it stores acc_ref. ie In the unspecified object instance returned from bind, it will have a member string acc_ref, not string& acc_ref. You have to use std::ref to make it really store a reference:
auto fProcRef = bind(fProcRefEx, ref(acc_ref), placeholders::_1);