Is it threadsave to pass shared_pointer by const reference? - c++

I often read I should pass a shared_ptr by const reference to a function, because its faster. When I think about it I am not sure if this is a realy good advice, because I am not sure if this would be threadsave. Can anybody tell me if this would be threadsave to pass by const ref?

You should prefer passing by const& to avoid the overhead of a copy of any class. For example, this is particularly important for stuff like std::string.
In the case of passing a shared_ptr by const& the overhead is mostly the incrementing and decrementing of the reference count because it's atomic.
BUT! There is a gotcha when creating a thread with const& shared_ptr: the reference count will be incremented. Since it is incremented, it's almost like if you had passed it by value. You are in fact passing by-value to the std::thread ctor, thus the increment, then passing by ref to the function.
See for yourself:
// gcc (Ubuntu 4.8.4-2ubuntu1~14.04.1) 4.8.4
void test(const shared_ptr<int>& i)
{
cout << "c = " << i.use_count() << endl;
}
int main(int argc, char** argv)
{
shared_ptr<int> i(new int);
cout << "c = " << i.use_count() << endl;
test(i);
cout << "thread!" << endl;
thread t(test, i);
t.join();
cout << "c = " << i.use_count() << endl;
}
The results being:
c = 1
c = 1
thread!
c = 2 // <= incremented!
c = 1 // <= thread joined!
shared_ptr is a perfect candidate for thread-safety but it will not protect you from race-conditions and dead-locks.

With a & you get the object itself.
You are not increased the ref count, you just pass over the object it self. The reason people suggest to pass by const& is to avoid the costs of thread-safely increasing the ref count. The cost when passing as a const& is same cost as copying an int or a pointer, and no temporary object will be bound to the const& as the constructor of the shared_ptr is marked explicit.
And because you will always have at least 1 reference to the object (the object the reference is bound to from the callee function) there is no fear the object might be destroyed while you use the local reference.

Passing by const reference is thread-agnostic. Look at following simple code:
void foo(const std::shared_ptr<int>& x) {
sleep(100000);
}
void bar() {
//...
std::shared_ptr y = ...;
foo(y);
}
y is going to live in the bar until foo returns - thus making sure shared_ptr remains valid. If foo() were to create another thread and pass x to it, it will have to copy it - and this will increase reference counter.

Passing by reference is always thread safe, no matter what object gets passed. And it is irrelevant if it is a reference to const or not.

Related

C++ map, use const reference as value type, what is the problem here?

Today I saw my boss's code which uses a const reference as a map's value type.
Here's the code:
class ConfigManager{
public:
map<PB::Point, const PB::WorldPoint&, compare_point> world_point;
//the rest are omitted
};
(PB is Google Protobuf, we are using the Protobuf library. I don't know much about it or if it's relevant to the question. )
What this class does is that it reads some config files and put it into some maps for searhing.
At first I was surprised because I haven't seen a map with a reference as its value, which is e.g. map<int, classA&> aMap.
So then I searched on SO and these 2 questions tell me that I can't do that.
C++: Is it possible to use a reference as the value in a map?
STL map containing references does not compile
Then I tried this code, indeed it doesn't compile:
Code Example1
struct A {
int x = 3;
int y = 4;
};
map<int, A&> myMap;
int main() {
A a;
myMap.insert(make_pair(1, a));
}
But if I change map<int, A&> myMap; to map<int, const A&> myMap;, it compiles.
Yet another problem occured. With map<int, const A&> myMap;, I can't use [] to get the pair, but I can use map.find().
(My boss told me to use map.find() after I told him using[] can't compile).
Code Example2
struct A {
int x = 3;
int y = 4;
};
map<int, const A&> myMap;
int main() {
A a;
myMap.insert(make_pair(1, a));
//can't compile
cout << myMap[1].x << " " << myMap[1].y << endl;
//can work
//auto it = myMap.find(1);
//cout << it->second.x << " " << it->second.y << endl;
}
So till here I was thinking my boss was correct. His code was correct.
The last story is that I showed the code to some online friends. And they noticed a problem.
Code Example3
#include <map>
#include <iostream>
#include <string>
using namespace std;
struct A {
int x = 3;
int y = 4;
~A(){
cout << "~A():" << x << endl;
x = 0;
y = 0;
}
};
map<string, const A&> myMap;
int main() {
A a;
cout << a.x << " " << a.y << endl;
myMap.insert(make_pair("love", a));
a.x = 999;
cout << "hello" << endl;
auto s = myMap.find("love");
cout << s->second.x << " " << s->second.y << endl;
}
The output is:
3 4
~A():3
hello
0 0
~A():999
If I understand the output correctly(correct me if I get it wrong), it indicates that:
make_pair("love", a) creates an object pair<"love", temproray copy of a>. And the pair gets inserted into myMap.
Somehow, I don't know how it happens, the temporary copy of a gets destructed immediately. To me, it means the memory of the temporary copy of a is now not owned by anyone and it is now a free space of memory that can be filled with any values, if I understand correctly.
So now I am getting confused again.
My questions are:
What happens to the Code Example3? Is my understanding correct? Why does temporary copy of a get destructed right after the statement? Isn't using a const reference can extend a temporary's lifetime? I mean, I think the it should not get destructed till main finishes.
Is my boss's code incorrect and very dangerous?
Why does temporary copy of a get destructed right after the statement?
Because (in most cases) that's how temporaries work. The live until the end of the statement in which they are created. The extension to a temporaries lifetime doesn't apply in this case, see here. The TLDR version is
In general, the lifetime of a temporary cannot be further extended by
"passing it on": a second reference, initialized from the reference to
which the temporary was bound, does not affect its lifetime.
can I use const reference as a map's value type?
Yes as long as you realise that adding a const reference to a map has no effect on the lifetime of the object being referred to. Your bosses code is also incorrect because the temporary returned by make_pair is destroyed at the end of the statement.
You may use std:: unique_ptr<A> instead. Then emplace instead of insert:
using value_t=std:: unique_ptr<A>;
std::map<int, value_t> myMap;
myMap.emplace(1,new A);
myMap[1]=new A{5,6};
myMap[1]->x=7;
more on std:: unique_ptr<A>:
https://en.cppreference.com/w/cpp/memory/unique_ptr
What happens to the Code Example3? Is my understanding correct?
Your explanation is close. The std::pair that is returned by std::make_pair is the temporary object. The temporary std::pair contains the copy of a. At the end of the expression the pair is destroyed, which also destroys its elements including the copy of a.
Why does temporary copy of a get destructed right after the statement? Isn't using a const reference can extend a temporary's lifetime? I mean, I think the it should not get destructed till main finishes.
The temporary here is the result of std::make_pair which is being used as an argument to the member function insert. The relevant rules that apply here are :
Whenever a reference is bound to a temporary or to a subobject thereof, the lifetime of the temporary is extended to match the lifetime of the reference, with the following exceptions:
[...]
a temporary bound to a reference parameter in a function call exists until the end of the full expression containing that function call [...]
[...]
source
The full expression containing the function call is the expression myMap.insert(make_pair(1, a)); This means that the lifetime of the result of std::make_pair ends after the function return, including the A it contains. The new std::map element will refer to the A in the temporary std::pair which will become dangling once insert returns.
Is my boss's code incorrect and very dangerous?
Yes, myMap contains a dangling references.

passing value to reference works one way [duplicate]

struct A {
A(int) : i(new int(783)) {
std::cout << "a ctor" << std::endl;
}
A(const A& other) : i(new int(*(other.i))) {
std::cout << "a copy ctor" << std::endl;
}
~A() {
std::cout << "a dtor" << std::endl;
delete i;
}
void get() {
std::cout << *i << std::endl;
}
private:
int* i;
};
const A& foo() {
return A(32);
}
const A& foo_2() {
return 6;
}
int main()
{
A a = foo();
a.get();
}
I know, returning references to local values is bad. But, on the other hand, const reference should extend a temporary object lifetime.
This code produce an UB output. So no life extention.
Why? I mean can someone explain whats happening step by step?
Where is fault in my reasoning chain?
foo():
A(32) - ctor
return A(32) - a const reference to local object is created and is returned
A a = foo(); - a is initialized by foo() returned value, returned value goes out of scope(out of expression) and is destroyed, but a is already initialized;
(But actually destructor is called before copy constructor)
foo_2():
return 6 - temp object of type A is created implicitly,a const reference to this object is created(extending its life) and is returned
A a = foo(); - a is initialized by foo() returned value, returned value goes out of scope(out of expression) and is destroyed, but a is already initialized;
(But actually destructor is called before copy constructor)
Rules of temporary lifetime extension for each specific context are explicitly spelled out in the language specification. And it says that
12.2 Temporary objects
5 The second context is when a reference is bound to a temporary. [...] A temporary bound to the returned value in a function return statement
(6.6.3) persists until the function exits. [...]
Your temporary object is destroyed at the moment of function exit. That happens before the initialization of the recipient object begins.
You seem to assume that your temporary should somehow live longer than that. Apparently you are trying to apply the rule that says that the temporary should survive until the end of the full expression. But that rule does not apply to temporaries created inside functions. Such temporaries' lifetimes are governed by their own, dedicated rules.
Both your foo and your foo_2 produce undefined behavior, if someone attempts to use the returned reference.
You are misinterpeting "until function exit". If you really want to use a const reference to extend the life of an object beyond foo, use
A foo() {
return A(32);
}
int main() {
const A& a = foo();
}
You must return from foo by value, and then use a const reference to reference the return value, if you wish to extend things in the way you expect.
As #AndreyT has said, the object is destroyed in the function that has the const &. You want your object to survive beyond foo, and hence you should not have const &
(or &) anywhere in foo or in the return type of foo. The first mention of const & should be in main, as that is the function that should keep the object alive.
You might think this return-by-value code is slow as there appear to be copies of A made in the return, but this is incorrect. In most cases, the compiler can construct A only once, in its final location (i.e. on the stack of the calling function), and then set up the relevant reference.

Reference count for a reference to shared_pointer

I understand that the shared_ptr class automatically manages dynamic objects.
Here, I have a function f that returns a const shared_ptr<int> to an int 2.
I have two versions of main that differ in one place. Version A saves the return value of f into a shared pointer, while version B saves into shared pointer reference.
using namespace std;
const std::shared_ptr<int> f() {
const std::shared_ptr<int> ret = std::make_shared<int>(2);
return ret;
}
int main () {
const std::shared_ptr<int> p = f(); // A
// const std::shared_ptr<int> &p = f(); // B
cout << p.use_count() << *p << endl; // prints 1 2
return 0;
}
Both versions print 1 2. I am okay with version A because p is the last shared_ptr pointing at the int, therefore use_count is 1.
Question: Why is use_count equal to 1 for version B? Where is the last existing shared_ptr?
In C++ if you assign a temporary directly into a && or const&, reference lifetime extension kicks in and the temporary lasts as long as reference does.
This only works when your reference is a local variable1.
1 Or in certain cases in certain compilers where you use aggregate construction of structs. But don't use that, because it is one of the spots where a "transparent" forwarding constructor behaves fundamentally different than an aggregate, so otherwise innocuous changes later can break your code.

How can I pass std::unique_ptr into a function

How can I pass a std::unique_ptr into a function? Lets say I have the following class:
class A
{
public:
A(int val)
{
_val = val;
}
int GetVal() { return _val; }
private:
int _val;
};
The following does not compile:
void MyFunc(unique_ptr<A> arg)
{
cout << arg->GetVal() << endl;
}
int main(int argc, char* argv[])
{
unique_ptr<A> ptr = unique_ptr<A>(new A(1234));
MyFunc(ptr);
return 0;
}
Why can I not pass a std::unique_ptr into a function? Surely this is the primary purpose of the construct? Or did the C++ committee intend for me to fall back to raw C-style pointers and pass it like this:
MyFunc(&(*ptr));
And most strangely of all, why is this an OK way of passing it? It seems horribly inconsistent:
MyFunc(unique_ptr<A>(new A(1234)));
There's basically two options here:
Pass the smart pointer by reference
void MyFunc(unique_ptr<A> & arg)
{
cout << arg->GetVal() << endl;
}
int main(int argc, char* argv[])
{
unique_ptr<A> ptr = unique_ptr<A>(new A(1234));
MyFunc(ptr);
}
Move the smart pointer into the function argument
Note that in this case, the assertion will hold!
void MyFunc(unique_ptr<A> arg)
{
cout << arg->GetVal() << endl;
}
int main(int argc, char* argv[])
{
unique_ptr<A> ptr = unique_ptr<A>(new A(1234));
MyFunc(move(ptr));
assert(ptr == nullptr)
}
You're passing it by value, which implies making a copy. That wouldn't be very unique, would it?
You could move the value, but that implies passing ownership of the object and control of its lifetime to the function.
If the lifetime of the object is guaranteed to exist over the lifetime of the call to MyFunc, just pass a raw pointer via ptr.get().
Why can I not pass a unique_ptr into a function?
You cannot do that because unique_ptr has a move constructor but not a copy constructor. According to the standard, when a move constructor is defined but a copy constructor is not defined, the copy constructor is deleted.
12.8 Copying and moving class objects
...
7 If the class definition does not explicitly declare a copy constructor, one is declared implicitly. If the class definition declares a move constructor or move assignment operator, the implicitly declared copy constructor is defined as deleted;
You can pass the unique_ptr to the function by using:
void MyFunc(std::unique_ptr<A>& arg)
{
cout << arg->GetVal() << endl;
}
and use it like you have:
or
void MyFunc(std::unique_ptr<A> arg)
{
cout << arg->GetVal() << endl;
}
and use it like:
std::unique_ptr<A> ptr = std::unique_ptr<A>(new A(1234));
MyFunc(std::move(ptr));
Important Note
Please note that if you use the second method, ptr does not have ownership of the pointer after the call to std::move(ptr) returns.
void MyFunc(std::unique_ptr<A>&& arg) would have the same effect as void MyFunc(std::unique_ptr<A>& arg) since both are references.
In the first case, ptr still has ownership of the pointer after the call to MyFunc.
Why can I not pass a unique_ptr into a function?
You can, but not by copy - because std::unique_ptr<> is not copy-constructible.
Surely this is the primary purpose of the construct?
Among other things, std::unique_ptr<> is designed to unequivocally mark unique ownership (as opposed to std::shared_ptr<> ).
And most strangely of all, why is this an OK way of passing it?
Because in that case, there is no copy-construction.
As MyFunc doesn't take ownership, it would be better to have:
void MyFunc(const A* arg)
{
assert(arg != nullptr); // or throw ?
cout << arg->GetVal() << endl;
}
or better
void MyFunc(const A& arg)
{
cout << arg.GetVal() << endl;
}
If you really want to take ownership, you have to move your resource:
std::unique_ptr<A> ptr = std::make_unique<A>(1234);
MyFunc(std::move(ptr));
or pass directly a r-value reference:
MyFunc(std::make_unique<A>(1234));
std::unique_ptr doesn't have copy on purpose to guaranty to have only one owner.
Since unique_ptr is for unique ownership, if you want to pass it as argument try
MyFunc(move(ptr));
But after that the state of ptr in main will be nullptr.
To piggyback the existing answers, C++ smart pointers are strongly related to the concept of ownership. Your code should clearly express your inensions related to passing or not the ownership to another component, i.e. function, thread or so, that's why there are unique, shared and weak pointers. The unique pointer already suggests that only one component has the ownership of that pointer at one moment, so that unique ownership cannot be shared, only moved. Namely the owner that currently owns the pointer will destroy it at its own will when getting out of context, usually at the end of the block or in a destructor. Passing an object to another function might imply sharing or moving the ownership. If you are sure that the owner don't get out of the context while calling the function by reference, calling by reference is possible, but it's just ugly. It adds some preconditions and postconditions and will increase the immobility of your code while it also breaks the smartness of the pointer which tuns into a more or less raw pointer. For example, changing the "function" to start and execute its own stuff in another thread is not longer possible. As passing by reference is still an option in some very contained parts of your code, i.e. looking for saving extra overhead of moving back and forth from unique to shared pointers, I would highly avoid it, especially in public interfaces.
Passing std::unique_ptr<T> as value to a function is not working because, as you guys mention, unique_ptr is not copyable.
What about this?
std::unique_ptr<T> getSomething()
{
auto ptr = std::make_unique<T>();
return ptr;
}
this code is working

c++ returning const reference of local variable

is it possible/ok to return a const reference even if the value the function returns is a local variable of this function? i know that locals are not valid anymore once the function returns - but what if the function is inlined and the returned value is only used within the callers scope? then the locals of the function should be included in the callers stackframe, no?
Don't count on it. Even if this works on 1 compiler, it's not standard supported behavior and is likely to break on others.
No, it's not OK. Local variables are declared on the stack, and the stack keeps changing between method calls. Also, the objects that get out of scope get destroyed. Always return a copy of a local variable.
Consider this code:
#include <iostream>
using namespace std;
class MyClass
{
public:
MyClass() { cout << "ctor" << endl; }
~MyClass() { cout << "dtor" << endl; }
MyClass(const MyClass& r) { cout << "copy" << endl; }
};
const MyClass& Test()
{
MyClass m;
return m;
}
int main()
{
cout << "before Test" << endl;
MyClass m = Test();
cout << "after Test" << endl;
}
This will print out:
before Test
ctor
dtor
copy
after Test
dtor
The object you're trying to copy has already called its destructor and may be in an invalid state.
inline is not a guarantee -- it's a suggestion. Even if you use tricks to force inline, you'll never be sure about the result, especially if you want to remain portable.
Hence, don't do it.
Doing that invokes undefined behaviour.
There's no way of forcing a compiler to inline the function. inline is just a suggestion - so is __forceinline
Even if you could guarantee that the function would be inlined, the destructor for the variable in question will still be executed, leaving you with a reference to a dead object.
And the big one - C++'s concept of the stack is delimited by scope - not by function.
#include <iostream>
int main()
{
{
int a = 5;
std::cout << std::hex << "0x" << &a << std::endl;
}
{
int b = 10;
std::cout << std::hex << "0x" << &b << std::endl;
}
}
My compiler puts 'a' and 'b' at different memory address. Except when I turn optimizations on. Yours may well decide that it's an optimization to reuse the memory your object previously occupied.
Is there a paticular problem you're trying to solve here? There are other ways of reducing the number of temporary objects created if that's your concern.
As others have noted, this is dangerous. It's also unnecessary, if your compiler supports the NRVO (Named Return Value Optimization), and your function uses and returns the local variable you would have liked to return by ref in a fairly simple way.
The NRVO allows the compiler to avoid copy construction under certain conditions - typically the main reason to avoid returning objects by value. VC++ 8 supports this (a delta on previous revisions) and it makes quite a bit of perf diff in frequently used code.
The value falls out of scope when the callee falls out of scope. So no, it is gone.
But if you want a fairly ugly solution (and a red flag warning you that your design might need refactoring), you can do something like this:
const MyObj& GetObj()
{
static const MyObj obj_;
return obj_;
}
...but this solution if fraught with peril, especially if the object is modifyable, or does something non-trivial in a multithreaded environment.
The inline keyword doesn't guarantee that the function is really inlined. Don't do it.