How to extend lifetime of member reference? - c++

Example code:
#include <iostream>
using namespace std;
struct Data {
int d;
};
class Foo {
public:
explicit Foo(const Data& data) : data_(data) {}
void PrintData() {
std::cout << data_.d << std::endl;
}
private:
const Data& data_;
};
Foo CreateFoo(int i) {
Data d;
d.d = i;
return Foo(d);
}
int main() {
Foo foo = CreateFoo(5);
foo.PrintData(); // prints 0 instead of 5
return 0;
}
https://ideone.com/KpCstl
foo.data_ is a reference to the Data created in CreateFoo which goes out of scope at the end of the function.
Without changing the definition of class Foo (e.g. to remove the reference), how can I extend the lifetime of d/data_, preferably so that it matches the lifetime of the Foo instance?
Is there perhaps some container object that I can wrap Foo in that will keep all its deps alive and return that from CreateFoo instead?
In the actual usage, our framework will keep Data alive, so it will outlive Foo as intended. But for my test code, I need to find another mechanism to keep Data alive at least until the test run is complete. I can use some kind of singleton to keep it alive, but I'm looking for a cleaner solution.

If you want to keep the reference in the member variable, then likely you will need to use dynamic allocation. Think about it -- the reference must point to some Data object somewhere that has the same lifetime as the Foo, but the Data object can't be stored with the Foo object, as the Foo object has no space for it, only for a reference. So generally in this case it is allocated on the heap. I would suggest using a unique_ptr to automatically manage this Data object:
#include <iostream>
#include <memory>
using namespace std;
struct Data {
int d;
};
class Foo {
public:
explicit Foo(const Data& data) : data_(std::make_unique<Data>(data)) {}
void PrintData() {
std::cout << data_->d << std::endl;
}
private:
std::unique_ptr<Data> data_;
};
Foo CreateFoo(int i) {
Data d;
d.d = i;
return Foo(d);
}
int main() {
Foo foo = CreateFoo(5);
foo.PrintData();
return 0;
}
Note that a unique_ptr allows only for one object to have ownership, which will disallow Foos from being copied unless you override the relevant methods. If you want copied Foos to have references to the same object, use shared_ptr and make_shared instead. If you want copied Foos to copy their Data objects, overload the copy constructor and operator=, or better yet, turn the Data pointer into a plain Data member.
If you don't want to have Foos own and manage their Datas, then I would suggest:
#include <iostream>
#include <memory>
using namespace std;
struct Data {
int d;
};
class Foo {
public:
explicit Foo(Data& data) : data_(data) {}
void PrintData() {
std::cout << data_->d << std::endl;
}
private:
Data& data_;
};
struct FooWithData {
Data foo_data;
Foo foo;
FooWithData(const FooWithData& other)
: foo_data(other.foo_data),
foo(foo_data) {}
FooWithData(FooWithData&& other)
: foo_data(other.foo_data),
foo(foo_data) {}
FooWithData& operator=(FooWithData& other) {
foo_data = other.foo_data;
}
};
FooWithData CreateFoo(int i) {
FooWithData ret;
ret.foo_data = Data { i };
ret.foo = Foo(ret.foo_data);
return ret;
}
int main() {
FooWithData foo_compound = CreateFoo(5);
foo_compound.foo.PrintData();
return 0;
}

This solution should work for you:
Foo CreateFoo(int i) {
static std::forward_list<Data> v;
v.push_front(Data{i});
return Foo(v.front());
}
Maintain a static container of Data objects, add a new object on every call to CreateFoo, and hand out references to the last inserted object.
Since the container is static you won't have lifetime issues. Also, a container of objects ensures that every call returns a Foo that binds to a unique Data object.
Also, note that you need a container that doesn't invalidate references to it on modification, so I chose std::forward_list.
Here's a demo.

If you want to keep your element alive even after scopes ends then you should use pointer, but again raw pointer is dangerous, so to counter this use std::shared_ptr, but then you wont be able to use ref variable to shared_ptr but an actual shared_ptr variable to keep the object of Data alive.
struct Data{
int data;
};
class Foo{
public:
explicit Foo(std:shared_ptr<Data>) {data = _data;}
Foo createFoo(){
shared_ptr<Data> d(new Data);
return Foo(d);//this thing makes sure your shared pointer gets copied and it does not go out of scope
}
std::shared_ptr<Data> data;
};

Related

Why don't reference members refer the assigned variable on nested vectors? What's a proper way to declare an alternative way to access a class member?

I would like to declare an alternative way to access a class member (an array position specifically), as in
class Foo {
int a[2];
int &a_first = a[0];
};
such that any access to a_first in a Foo instance is for all purposes equivalent to accessing a[0] of that same instance.
The code above works as I expected with singular instances and single vectors of the class, but when used on a nested vector the reference address differs from the member address:
#include <iostream>
#include <vector>
class A {
public:
int m;
int &mref = m;
};
int main()
{
A a;
std::cout << (&a.m == &a.mref) << '\n'; // output: 1
std::vector<A> av1(1);
std::cout << (&av1[0].m == &av1[0].mref) << '\n'; // output: 1
std::vector<std::vector<A>> av2(1, std::vector<A>(1));
std::cout << (&av2[0][0].m == &av2[0][0].mref) << '\n'; // output: 0
return 0;
}
I thought reference variables acted as aliases of their assigned variable and were resolved at compile time without being assigned any actual memory at runtime, unlike pointers. Why is this not consistent with the behavior displayed above? What would be a correct way to achieve the alias I want?
The code above works as I expected
Actually it doesn't:
class A {
public:
int m{};
int &mref = m;
};
int main()
{
A a;
A a2 = a;
std::cout << (&a2.m == &a2.mref) << '\n'; // output: 0
};
A reference can be bound only on initialization. Copying will copy the value, not re-bind the reference. So any copy of an object of type A will mess up your reference. This is what happens in your nested vector example. You don't need a nested vector to see this. Try and push in a vector<A>, the vector will have to resize and during the resize will copy its elements, messing your reference.
I thought reference variables acted as aliases of their assigned variable ...
True
... and were resolved at compile time without being assigned any actual memory at runtime, unlike pointers.
Not always. You cannot always resolve at compile time the reference, in which case the reference will actually be implemented with a pointer behind the scenes.
Possible solutions:
use std::reference_wrapper, the copy assignment operator rebinds the reference, but you will need to implement custom copy constructor/assignments for your class:
class A {
public:
int m{};
std::reference_wrapper<int> mref = m;
A() = default;
A(const A& other) noexcept
: m{other.m},
mref{m}
{}
A& operator=(const A& other) noexcept
{
m = other.m;
mref = m;
return *this;
}
};
use a method that returns a reference to the variable
class A {
public:
int m{};
int& mref() { return m; }
const int& mref() const { return m; }
};

Pointer from vector causes BAD_ACCESS error when created from object reference

I have a use case where I need to store pointers to object in vector
class Foo {
void hello() {}
}
vector<Foo*> my_vector
// There is an object up the chain that takes in Foo
bar.setFoo(Foo())
void setFoo(Foo foo) {
my_vector.emplace_back(&foo);
}
// down the chain
my_vector[0]->hello() // Throws BAD_ACCESS as I see there is nothing in memory address
For Testing if I change to this it works fine:
// change setFoo to take a pointer
bar.setFoo(new Foo());
void setFoo(Foo *foo) {
my_vector.emplace_back(foo);
}
my_vector[0]->hello() // works
I cannot really use new Foo as I don't control that interface plus have to manually delete
How can I come up with a solution where I can create vector of pointers from object references and safely use them
Because your setFoo function takes its argument by value, that argument is a local variable. A variable whose life-time ends immediately when the function returns.
The pointer you save in the vector will become invalid as soon as setFoo returns. Dereferencing that pointer leads to undefined behavior.
The natural solution is to not store pointers in the vector. And if it's a requirement then you need to allocate the objects dynamically.
My recommendation if you must use pointers (for example because of polymorphism) is to use a smart pointer like std::unique_ptr.
Also even with pointer you can pass the argument to setFoo by value, using copy-semantics:
vector<std::unique_ptr<Foo>> my_vector;
void setFoo(Foo foo) {
my_vector.emplace_back(std::make_unique<Foo>(foo));
}
Can you post your code snippet that is producing the error?
At a glance, it looks like this function may not be working since you are storing the address of a Foo object that goes out of scope
void setFoo(Foo foo) {
my_vector.emplace_back(&foo);
}
This second one probably works because you are creating Foo dynamically (from the heap) somewhere else
void setFoo(Foo *foo) {
my_vector.emplace_back(foo);
}
Above, you are storing the address. If you are performing your clean up (deleting foo) somewhere else, then you won't need to clean it up here.
How can I come up with a solution where I can create vector of
pointers from object references and safely use them
You can use std::vector<std::unique_ptr<Foo>>.
Here is an example:
#include <memory>
#include <vector>
#include <iostream>
class Foo
{
public:
virtual ~Foo() {}
virtual void hello() { std::cout << "Hello\n"; }
};
class Foo2 : public Foo
{
public:
void hello() { std::cout << "Hello2\n"; }
};
std::vector<std::unique_ptr<Foo>> my_vector;
template <typename T>
void addFoo(const T& f)
{
auto ptr = std::make_unique<T>(f);
my_vector.emplace_back(std::move(ptr));
}
int main()
{
Foo fBase;
Foo2 fDerived;
addFoo(fBase);
addFoo(fDerived);
for (auto& v : my_vector)
v->hello();
}
Output:
Hello
Hello2
At the end, the pointers are automatically deleted. If you require pointers to be shared, then use std::vector<std::shared_ptr<Foo>>.

My lambda does not correctly convert the captured 'this' during copy construction

I've narrowed down my problem to exactly this
#include <iostream>
#include <functional>
struct Foo {
std::function<Foo*()> lambda;
Foo()
:lambda([this](){return this;})
{}
};
int main(){
Foo a;
Foo b = a;
std::cout << &a << " " << a.lambda() << std::endl;
std::cout << &b << " " << b.lambda() << std::endl;
}
where the output is
0x7ffd9128b8a0 0x7ffd9128b8a0
0x7ffd9128b880 0x7ffd9128b8a0
I originally expected that this would always point to the instance that owned the lambda. However I forgot about copy construction. In this case the lambda captures this and then it is fixed and no matter how many times the lambda is copied it points to the original value of this.
Is there a way fix this so that lambda always has a reference to it's owning object this even under copy construction of the owning object.
Sounds like you need to provide your own special member functions, no? E.g., for the copy constructor:
Foo(const Foo& other)
:lambda([this](){return this;})
{}
Whilst #lubgr answered the question for what I asked I think it is worth noting the other solution I have for my exact problem. The question stemmed from building a class to encapsulate lazy initialisation of members. My original attempt was
template <typename T>
class Lazy {
mutable boost::once_flag _once;
mutable boost::optional<T> _data;
std::function<T()> _factory;
void Init() const { boost::call_once([&] { _data = _factory(); }, _once); }
public:
explicit Lazy(std::function<T()> factory):_once(BOOST_ONCE_INIT),_factory(factory){}
T& Value() {
Init();
return *_data;
}
};
which can be used like
class Foo {
int _a;
Lazy<int> _val;
Foo(a):_a(a):_val([this](){return this->_a+1;}){}
}
Foo f(10);
int val = f._val.Value();
but has the same problem that I asked in my question in that this is a circular reference that doesn't get preserved for copy construction. The solution is not to create a custom copy constructor and possibly move constructor but to fix the Lazy implementation class so that we can pass in an arg to the factory.
The new implementation of Lazy for members is
template <typename T, typename TThis>
class LazyMember {
mutable boost::once_flag _once;
mutable boost::optional<T> _data;
typedef std::function<T(TThis const*)> FactoryFn;
FactoryFn _factory;
void Init(TThis const * arg0) const { boost::call_once([&] { _data = _factory(arg0); }, _once); }
public:
explicit LazyMember(FactoryFn factory):_once(BOOST_ONCE_INIT),_factory(factory){}
T& Value(TThis const * arg0) { Init(arg0); return *_data; }
T const & Value(TThis const * arg0) const { Init(arg0); return *_data; }
};
which is used as
class Foo {
int _a;
Lazy<int> _val;
Foo(a):_a(a):_val([](Foo const * _this){return _this->_a+1;}){}
}
Foo f(10);
int val = f._val.Value(&f);
and this doesn't have the circular reference problems and thus doesn't require a custom copy/move constructor.

Vector of fn pointers doesn't reveal correct size()

I have a vector of function pointers that is default initialized as a class member, so it always has 2 elements. However, when reading its size() I get gibberish. I have created a trivial, minimal replication of this problem below.
#include <iostream>
#include <vector>
using fnptr = float (*)(float);
float fn1(float x){ return x;};
float fn2(float x){ return x;};
std::vector<fnptr> fnptrs(){
std::vector<fnptr> ret;
ret.push_back(&fn1);
ret.push_back(&fn2);
return ret;
}
class Foo{
public:
int nFns()const{
return fns.size();
}
private:
std::vector<fnptr> fns = fnptrs(); // always initialized
};
class Baz {
public:
explicit Baz(Foo f):foo{&f}{}
const Foo* foo; // my problem does not appear if this is not a pointer
};
class Bar {
public:
explicit Bar(Foo f):foo{f}{
bazs.push_back(Baz(foo));
bazs.push_back(Baz(foo));
}
void viewSizes()const{
for (auto& i:bazs)
std::cout << " i.foo->nFns() = " << i.foo->nFns() << "\n";
}
const Foo foo;
std::vector<Baz> bazs;
};
int main(int argc, const char * argv[]) {
Foo f;
Bar b(f);
b.viewSizes();
return 0;
}
Output:
i.foo->nFns() = -677271344
i.foo->nFns() = -516
I do not prefer to store Foo in Baz as a pointer, but in my real program, I have a vector of Baz's that is swapped out frequently and a reference cannot be used for that (doesn't compile). If I make it a regular member (not a pointer or reference) then I have no problems, but with large numbers of these objects its better not to store the same copy in every object, so a pointer is what I need to use.
Here:
explicit Baz(Foo f):foo{&f}{}
f is a local variable of the constructor and evaporates once the constructor exits. You probably want to pass a reference, or an actual raw pointer instead.

Is this lvalue returning dangerous?

I have this code:
struct Base {};
struct Derived : public Base {
int somedata;
};
std::unique_ptr<Base> createTemporary() {
return std::make_unique<Derived>(); // This code has been simplified
}
template<typename T>
T& convertValueTo(std::unique_ptr<Base>&& obj) {
return static_cast<T&>(*obj);
}
int main() {
int data = convertValueTo<Derived>(createTemporary()).somedata;
return 0;
}
I designed the convertValueTo templated function to return the asked type of the object and mostly for function calls like
auto some_value = convertValueTo<Something_Else>(createTemporary()).a_member_variable;
Now I'm wondering.. is there a safer way to do this? If someone were to use the returned reference from convertValueTo, the temporary will be destroyed as soon as the line expression ends right?
My goal is:
Allow the use of a temporary and destroy it as soon as possible if the reference is not stored (as above)
Allow a safe reference binding to a valid object in case someone wants to
Convert to a unique_ptr of the required type. Then it is clear who has ownership of the dynamically created object, namely the unique_ptr returned from the conversion function. As soon as you create an lvalue reference to the dynamically created object, there will be the possibility that the reference survives the lifetime of your object.
#include <memory>
struct Base {};
struct Derived : public Base {
int somedata;
};
std::unique_ptr<Base> createTemporary() {
return std::make_unique<Derived>(); // This code has been simplified
}
template<typename T>
std::unique_ptr<T> convertValueTo(std::unique_ptr<Base>&& obj) {
auto ptr = obj.release ();
return std::unique_ptr<T> { static_cast<T*>(ptr) };
}
int main() {
int data = convertValueTo<Derived>(createTemporary())->somedata;
return 0;
}