I tried to build a very simple version of std::function. Below the code of a very first version. My question is about the lifetime of the temporary object from the lambda-expression, because I'm actually storing a reference to it. Or is the lifetime of the object prolonged?
It tried to use a copy of the function (T mf instead of const T& mf inside struct F), but that gives an error due to the decaying of the function to a pointer.
#include <iostream>
template<typename T>
struct F {
F(const T& f) : mf{f} {
std::cout << __PRETTY_FUNCTION__ << '\n';
}
void test() {
std::cout << __PRETTY_FUNCTION__ << '\n';
mf();
}
const T& mf;
};
template<typename T>
struct F<T*> {
F(T f) : mf{f} {
std::cout << __PRETTY_FUNCTION__ << '\n';
}
void test() {
std::cout << __PRETTY_FUNCTION__ << '\n';
mf();
}
T* mf;
};
void g() {
std::cout << __PRETTY_FUNCTION__ << '\n';
}
int main() {
F<void(void)> f1(g);
f1.test();
auto g1 = g;
F f2(g1);
f2.test();
F f3([](){ // lifetime?
std::cout << __PRETTY_FUNCTION__ << '\n';
});
f3.test();
auto l1 = [](){
std::cout << __PRETTY_FUNCTION__ << '\n';
};
F f4(l1);
f4.test();
}
You do have lifetime problems here: lifetime extension thanks to const only applies for local const references.
You need to make sure that the referenced function lives at least as long as the wrapper, or you need to copy/move the function into the wrapper.
but that gives an error due to the decaying of the function to a pointer
You can use std::decay_t<T> to ensure that you're copying/moving objects (e.g. closures) into the wrapper.
Related
I have a class in which I overloaded method. Depending on lifetime of the object given method will be called.
How can I call fun implementation for temporary objects? I thought that std::move() casts its argument to an rvalue. Could you tell me why the code below does not work as intended?
template <typename T>
void fun(T&& arg) {
arg.callamethod();
}
class TestCall {
private:
public:
void callamethod() && {
std::cout << "R VALUE REF" << std::endl;
}
void callamethod() const & {
std::cout << "CONST L VALUE REF" << std::endl;
}
void callamethod() & {
std::cout << "L VALUE REF" << std::endl;
}
};
int main() {
TestCall arg = TestCall();
const TestCall arg2 = TestCall();
fun(arg);
fun(arg2);
fun(std::move(arg)); // calls callamethod() &
fun(std::move(arg2)); // calls callamethod() const &
}
Change your function fun to the following code and you will see that rvalue qualifier on your method callamethod works as expected.
template <typename T>
void fun(T&& arg) {
std::forward<T>(arg).callamethod();
}
Note that the last call:
fun(std::move(arg2));
resolves to the const lvalue option of callamethod as you do not have a const rvalue qualifier option, if you add one it will go to it.
This question already has answers here:
const reference to a temporary object becomes broken after function scope (life time)
(2 answers)
Closed 4 years ago.
For regular local const reference variables, the scope is prolonged. Which is why the following code works as expected:
#include <iostream>
#include <memory>
struct foo
{
foo()
{
std::cout << "foo() #" << (void*)this << std::endl;
}
~foo()
{
std::cout << "~foo() #" << (void*)this << std::endl;
}
};
int main()
{
auto const& f = std::make_shared<foo>();
std::cout << "f = " << f.get() << std::endl;
return 0;
}
// prints:
// foo() #0x55f249c58e80
// f = 0x55f249c58e80
// ~foo() #0x55f249c58e80
It seems though that this does not work as expected when assigning a moved object using std::move():
#include <iostream>
#include <memory>
#include <list>
struct foo
{
foo()
{
std::cout << "foo() #" << (void*)this << std::endl;
}
~foo()
{
std::cout << "~foo() #" << (void*)this << std::endl;
}
};
int main()
{
std::list<std::shared_ptr<foo>> l;
l.push_back(std::make_shared<foo>());
auto const& f = std::move(l.front());
l.clear();
std::cout << "f = " << f.get() << std::endl;
return 0;
}
// prints
// foo() #0x564edb58fe80
// ~foo() #0x564edb58fe80
// f = 0x564edb58fe80
Does std::move() indeed change the scope, or am I dealing with a compiler bug?
Changing the variable from auto const& f to just auto f fixes the problem. If I wrap the move into another function, it also works:
auto const& f = [&]() { return std::move(l.front()); }();
It's almost like std::move() does not share the same semantics as a function call, but rather as if it was just a regular variable assignment:
auto const& f = std::move(l.front());
Let's put aside std::move() and create this function:
struct sometype {};
const sometype &foobar( const sometype &cref ) { return cref; }
and now we use it:
const sometype &ref = foobar( sometype() );
What do you think, will lifetime of temporary be prolongated in this case? No it would not. Lifetime is only prolongated when you assign to a reference directly. When it goes through a function or through static_cast or std::move that prolongation is gone. So you have exactly the same issue with std::move()
struct sometype {
sometype() { std::cout << "ctype()" << std::endl; }
~sometype() { std::cout << "~ctype()" << std::endl; }
};
const sometype &foobar( const sometype &cref ) { return cref; }
int main()
{
const sometype &cref1 = foobar( sometype() );
sometype &&cref2 = std::move( sometype() );
std::cout << "main continues" << std::endl;
}
output:
ctype()
~ctype()
ctype()
~ctype()
main continues
live example
Note: you should not use std::move() on return statement, it does not give you anything.
For your code change. You should remember that std::move() does not move anything. It is done by special assignment operator or constructor (if they provided for a type). So when you write this code:
const type &ref = std::move( something );
there is no constructor nor assignment operator involved and so no moving happens. For actual moving to happen you have to assign it to a variable:
type val = std::move( something );
now moving would happen if possible or copy otherwise.
May be duplicated to this.
I read Effective Modern C++. Under Item 1, I found a case for universal reference:
For the last example f(27); I did a test under VS2013.
void process(int& x)
{
std::cout << "int&" << std::endl;
}
void process(int&& x)
{
std::cout << "int&&" << std::endl;
}
template<typename T>
void f(T&& param)
{
std::cout << "------------------------------------------------" << std::endl;
if (std::is_lvalue_reference<T>::value)
{
std::cout << "T is lvalue reference" << std::endl;
}
else if (std::is_rvalue_reference<T>::value)
{
std::cout << "T is rvalue reference" << std::endl;
}
else
{
std::cout << "T is NOT lvalue reference" << std::endl;
}
std::cout << "param is: " << typeid(param).name() << std::endl;
process(std::forward<T>(param));
process(param);
}
int getINT()
{
int x = 10;
return x;
}
int _tmain(int argc, _TCHAR* argv[])
{
f(10);
f(getINT());
return 0;
}
Here is the output:
------------------------------------------------
T is NOT lvalue reference
param is: int
int&&
int&
------------------------------------------------
T is NOT lvalue reference
param is: int
int&&
int&
I found that within the template function, without std::forward<T>(param), process(int& x) will be called but according to the book, the type for param should be rvalue reference, so process(int&& x) should be called. But this is not the case. Is it I misunderstand something?
Here is the forwarding reference I found from other thread:
You're confusing types with value categories. As a named parameter, param is an lvalue, then for process(param); process(int& x) will be called.
That's why we should use std::forward with forwarding reference; std::forward<T>(param) converts param to rvalue for this case, then process(int&& x) will be called (as expected).
I'm trying to make a function that mimics Python's with statement but I've run into some interesting behavior that I don't quite understand.
With the following program:
#include <iostream>
struct foo {
foo() { std::cout << "foo()" << std::endl; }
~foo() { std::cout << "~foo()" << std::endl; }
};
auto make_foo() -> foo {
return {};
}
template <typename T, typename F>
auto with(T&& t, F&& fn) -> void {
fn(std::forward<T>(t));
}
auto main() -> int {
std::cout << "before" << std::endl;
with(make_foo(), [](auto f) {
std::cout << "during" << std::endl;
});
std::cout << "after" << std::endl;
}
When compiled under with the clang provided by Xcode 6.3 and -std=c++14 and run I get the following output:
before
foo()
during
~foo()
~foo()
after
Does anybody know why I am getting two ~foo()'s in my output?
Here are the two objects:
with(make_foo(), [](auto f) {
1^^^^^^^^^ 2^^^^^^
There is the object returned by make_foo(), and the function argument f.
If you pass by reference (change to auto&& f) then you will only see evidence of one object.
There's no creation message because this is created by copy/move construction and you do not have any output in those constructors.
Note that there may be more objects inside make_foo() but your compiler is doing copy elision.
Your destructor calls don't appear to be matched with constructor calls simply because you aren't tracing copy/move constructors. If we add the tracing like so:
struct foo {
foo() { std::cout << "foo()" << std::endl; }
~foo() { std::cout << "~foo()" << std::endl; }
foo(const foo&) { std::cout << "foo(const foo&)" << std::endl; }
foo(foo&&) { std::cout << "foo(foo&&)" << std::endl; }
};
our output is now:
before
foo()
foo(foo&&)
during
~foo()
~foo()
after
The reason for the move-construction is that your lambda takes its parameter by value:
[](auto f) {
// ^^^^^^
std::cout << "during" << std::endl;
}
If you don't want the copy, take by reference-to-const, or maybe even forwarding reference.
This works for me by accepting an r-reference in the lambda function parameter to prevent a copy being made:
#include <iostream>
struct foo {
foo() { std::cout << "foo()" << std::endl; }
~foo() { std::cout << "~foo()" << std::endl; }
};
auto make_foo() -> foo {
return {};
}
template <typename T, typename F>
auto with(T&& t, F&& fn) -> void {
fn(std::forward<T>(t));
}
auto main() -> int {
std::cout << "before" << std::endl;
with(make_foo(), [](auto&&) { // r-reference!
std::cout << "during" << std::endl;
});
std::cout << "after" << std::endl;
}
New Improved Output:
before
foo()
during
~foo()
after
Suppose you have to write a template library that operates on a client-supplied type. Also, suppose that this library really needs to access a global variable parameterized by the client-supplied type. How would you go about implementing this pattern?
#AnonymousCoward
this is derived from your solution. note the initialization/destruction patterns in this variant (read the output if you don't have it memorized already). you can use this to defer creation until access, and to destruct and free at termination (which may be useful for custom types):
#include <iostream>
#include <memory>
class t_custom {
public:
t_custom() {
std::cout << "custom ctor: " << __PRETTY_FUNCTION__ << "\n";
}
~t_custom() {
std::cout << "custom dtor: " << __PRETTY_FUNCTION__ << "\n";
}
};
template<typename T>
class Global {
public:
static T* Shared() {
std::cout << "enter: " << __PRETTY_FUNCTION__ << "\n";
static std::auto_ptr<T>pVar(new T);
std::cout << "access: " << __PRETTY_FUNCTION__ << ":\t\t";
return pVar.get();
}
private:
Global();
~Global();
Global(const Global& rhs);
Global& operator=(const Global& rhs);
};
template<typename T>
class Global_orig {
public:
static T* const pVar;
private:
Global_orig();
Global_orig(const Global_orig& rhs);
Global_orig& operator=(const Global_orig& rhs);
};
template<typename T>T* const Global_orig<T>::pVar(new T); // << oh no! global construction
int main(int argc, char* const argv[]) {
std::cout << ">> main: " << __PRETTY_FUNCTION__ << "\n\n";
std::cout << Global<float>::Shared() << "\n";
std::cout << Global<int>::Shared() << "\n";
std::cout << Global<t_custom>::Shared() << "\n";
std::cout << Global_orig<t_custom>::pVar << "\n";
std::cout << "\n<< main: " << __PRETTY_FUNCTION__ << "\n\n";
return 0;
}
it may also be a good idea for the client to supply a factory functor for you, rather than forcing them to use T's default initializer.
Here is the solution I use to emulate this behavior:
template <typename T> class Global {
public:
static T *pVar;
private:
Global() {}
Global(const Global& rhs) {}
void operator=(const Global& rhs) {}
};
template <typename T> T* Global<T>::pVar = new T;
It seems to do what I want for my particular application. Is there any problem that restricts its applicability?