Reputation: 93
I was developing some class and bumped for this question. Consider I have following class:
struct A
{
int *p;
A()
{
p = new int(1);
cout << "ctor A" << endl;
}
A(const A& o)
{
cout << "copy A" << endl;
p = new int(*(o.p));
}
A(A&& o)
{
cout << "move A" << endl;
p = std::move(o.p);
o.p = NULL;
}
A& operator=(const A& other)
{
if (p != NULL)
{
delete p;
}
p = new int(*other.p);
cout << "copy= A" << endl;
return *this;
}
A& operator=(A&& other)
{
p = std::move(other.p);
other.p = NULL;
cout << "move= A" << endl;
return *this;
}
~A()
{
if(p!=NULL)
delete p;
p = NULL;
cout << "dtor A" << endl;
}
};
And following class which has A
as a property:
class B {
public:
B(){}
A myList;
const A& getList() { return myList; };
};
And this function which checks for some variable value and returns different objects in different cases:
B temp;
A foo(bool f)
{
A a;
*a.p = 125;
if (f)
return a;
else
{
return temp.getList();
}
}
Now, I am want to use this function like this:
A list1 = foo(true);
if(list1.p != NULL)
cout << (*list1.p) << endl;
cout << "------"<<endl;
A list2 = foo(false);
if (list2.p != NULL)
cout << (*list2.p) << endl;
The purpose of this situation is:
Function foo
should return (or move) without copying some local object with changes in p
if argument is true
, or should return property of global variable temp
without calling copy constructors of A
(i.e. return reference of myList
) and also it should not grab myList
from B
(it should not destroy myList
from B
, so std::move
can not be used) if argument is false
.
My question is:
How should i change function foo
to follow upper conditions? Current implementation of foo
works right in true
case and moving that local variable, but in case false
it calls copy constructor for list2
. Other idea was to somehow extend lifetime of local variable, but adding const reference did not work for me. Current output is:
ctor A
ctor A
move A
dtor A
125
------
ctor A
copy A
dtor A
1
dtor A
dtor A
dtor A
Upvotes: 9
Views: 2461
Reputation: 15334
The simplest solution is probably to use std::shared_ptr
as in Jarod42's answer. But if you want to avoid smart pointers, or if you can't change B
you can probably create your own wrapper class that might or might not own an A
. std::optional
might be quite convenient for this:
class AHolder {
private:
std::optional<A> aValue;
const A& aRef;
public:
AHolder(const A& a) : aRef(a) {}
AHolder(A&& a) : aValue(std::move(a)), aRef(aValue.value()) {}
const A* operator->() const { return &aRef; }
};
The class contains an optional
to own the A
if required and you can use move-semantics to move it in. The class also contains a reference (or pointer) that either references the contained value or references another object.
You can return this from foo
:
AHolder foo(bool f)
{
A a;
*a.p = 125;
if (f)
return a;
else
{
return temp.getList();
}
}
And the caller can access the contained reference:
auto list1 = foo(true);
if(list1->p != nullptr)
cout << (*list1->p) << endl;
cout << "------"<<endl;
auto list2 = foo(false);
if (list2->p != nullptr)
cout << *list2->p << endl;
If you don't have access to std::optional
there is boost::optional
or you could use std::unique_ptr
at the cost of a dynamic memory allocation.
Upvotes: 2
Reputation: 304
Your function foo returns an instance of A, not a reference (nor a pointer), so you can`t get access to B.myList content without copying or moving.
In order to have this access you should either use smart pointers (like Jarod42 wrote) or just simple pointers like this:
B temp;
A* foo(bool f)
{
if (f)
{
A* ptr = new A;
*ptr->p = 125;
return ptr;
}
else
{
return &temp.getList();
}
}
However this particular code will not work coz .getList() returns const reference but foo returns non-const pointer (this could but should not be dirty hacked with const_cast<>).
Generally you need to choose what exactly the foo function should return:
If you have to make this decision on runtime (for example by your bool parameter) then the pointers (simple or smart - whatever) are the only option (also remember to delete manually allocated memory).
Upvotes: 0
Reputation: 218343
If you can change B
to
class B {
public:
B(){}
std::shared_ptr<A> myList = std::make_shared<A>();
const std::shared_ptr<A>& getList() const { return myList; };
};
then foo
can be:
B b;
std::shared_ptr<A> foo(bool cond)
{
if (cond) {
auto a = std::make_shared<A>();
*a->p = 125;
return a;
} else {
return b.getList();
}
}
Output is
ctor A
ctor A
125
------
1
dtor A
dtor A
Upvotes: 4