Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Understanding C++ std::shared_ptr when used with temporary objects

I am trying to understand the use of a shared_ptr p when it is used in the construction of an unnamed shared_ptr and the effects this has on p. I was toying with my own examples and wrote the following piece of code:

shared_ptr<int> p(new int(42));
cout << p.use_count() << '\n';          
{ 
  cout << p.use_count() << '\n';
  shared_ptr<int>(p);
  cout << p.use_count() << '\n';
}
cout << p.use_count() << '\n';

Output:
1
1
0
1
  1. Is it correct that line 5, uses p to create a temp. shared_ptr (i.e an unnamed shared_ptr)?
  2. If so why isn't the use_count increased. Is the temp.object destroyed even before we exit the block at line 7.
  3. If it is destroyed and p's use count becomes zero inside the block, how come it is 1 again after we exit the block?

If I would have used a named shared_ptr q on line 5, i.e:

shared_ptr<int>q(p);

Everything would work as expected, inside the block after line 5 the use count would be 2 and after we exit the block it would be 1 again.

like image 648
Sohof Avatar asked Oct 16 '25 05:10

Sohof


2 Answers

According to the C++ Standard (8.5.1.3 Explicit type conversion (functional notation))

1 A simple-type-specifier (10.1.7.2) or typename-specifier (17.7) followed by a parenthesized optional expressionlist or by a braced-init-list (the initializer) constructs a value of the specified type given the initializer...

So the expression in this expression statement

shared_ptr<int>(p);

looks like an explicit type conversion (functional) expression.

On the other hand, a declarator in a declaration can be enclosed in parentheses. For example

int ( x );

is a valid declaration.

So this statement

shared_ptr<int>(p);

can be interpretated as a declaration like

shared_ptr<int> ( p );

So there is an abiguity.

The C++ Standard resolves this ambiguity the following way (9.8 Ambiguity resolution)

1 There is an ambiguity in the grammar involving expression-statements and declarations: An expression statement with a function-style explicit type conversion (8.5.1.3) as its leftmost subexpression can be indistinguishable from a declaration where the first declarator starts with a (. In those cases the statement is a declaration.

Thus this statement in the inner code block

shared_ptr<int>(p);

is a declaration of a new shared pointer with the name p that hides the previous declaration of the object with the same name p in the outer code block and that is created by using the defalut constructor

constexpr shared_ptr() noexcept;

According to the description of this constructor

2 Effects: Constructs an empty shared_ptr object.

3 Postconditions: use_count() == 0 && get() == nullptr.

If you want to deal with an expression instead of the declaration then all you need to do is to enclose the body of the statement in parentheses getting a primary expression in an expression statement.

Here is a demonstrative program.

#include <iostream>
#include <memory>

int main() 
{
    std::shared_ptr<int> p( new int( 42 ) );

    std::cout << "#1: " << p.use_count() << '\n';          

    { 
        std::cout << "#2: " << p.use_count() << '\n';

        ( std::shared_ptr<int>( p ) );

        std::cout << "#3: " << p.use_count() << '\n';
    }

    std::cout << "#4: " << p.use_count() << '\n';

    return 0;
}

In this case its output is

#1: 1
#2: 1
#3: 1
#4: 1
like image 179
Vlad from Moscow Avatar answered Oct 17 '25 17:10

Vlad from Moscow


  1. no

In line 5 you create new variable p. Empty one. See this:

shared_ptr<int> p(new int(42));
cout << p.use_count() << '\n';
cout << "address " << &p << "\n";
{
    cout << p.use_count() << '\n';
    shared_ptr<int>(p);
    cout << "address " << &p << "\n";
    cout << p.use_count() << '\n';
}
cout << p.use_count() << '\n';

output:

1
address 0x7ffcf3841860
1
address 0x7ffcf3841870
0
1

Note, that address of p has changed.

To fix it change parenthesis:

shared_ptr<int> {p};
like image 44
Radosław Cybulski Avatar answered Oct 17 '25 17:10

Radosław Cybulski



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!