1

I was experimenting with braced initialisation and specifically using value initialisation.

To my understanding int x {} uses value initialisation to set the value of x to 0.

So I then tried this with a std::shared_ptr and a trivial inner class Foo: std::shared_ptr<Foo> sharedFoo {} and tried to call a function from sharedFoo. I was slightly surprised to see this resulted in a segmentation fault.

My intuition had made me think that this would be value initialised, using the default constructor for Foo. However, I then realised that sharedFoo has not actually had any memory allocated to it, using either new Foo or std::make_shared. Is my reasoning correct here. Is my reasoning correct here? I was hoping to default initialise a shared_ptr using simple braces.

My full code for reference is below:

    #include <memory>
    #include <iostream>
    
    class Foo
    {
    public:
        int getValue() const { return value; }
    private:
        int value = 0;
    };
    
    int main()
    {
        int i {};
    
        std::shared_ptr<Foo> sharedFoo { };
    
        std::cout << i << std::endl;
    
        std::cout << sharedFoo->getValue() << std::endl; //SEGFAULT
    
        return 0;
    }

For reference, my question is not the following: std::shared_ptr initialization: make_shared<Foo>() vs shared_ptr<T>(new Foo)

Really, the core of the question is why int i {} performs a value initialisation of i, but std::shared_ptr<Foo> sharedFoo {} does a default initialisation of sharedFoo and doesn't value initialise the contained pointer.

8
  • x86-64 gcc trunk. I've also updated the question to remove new Foo Commented Jul 11, 2023 at 14:01
  • Ok, that makes it clear. Thanks. Commented Jul 11, 2023 at 14:04
  • Does this answer your question? std::shared_ptr initialization: make_shared<Foo>() vs shared_ptr<T>(new Foo) The question is a bit different, but the answer is applicable here as well. Commented Jul 11, 2023 at 14:08
  • @zerocukor287, no sorry that question is about the difference between std::make_shared and std::shared_ptr<Foo>(new Foo). My question is about value initialisation of a std::shared_ptr Commented Jul 11, 2023 at 14:09
  • 3
    The core of your confusion seems to be that a shared_ptr (or even a raw pointer) is an object in its own right. It is not the object it points to (if any). If you value-initialized a raw pointer that would also be nullptr. Commented Jul 11, 2023 at 14:16

3 Answers 3

2

To my understanding int x {} uses value initialisation to set the value of x to 0.

Correct!

Really, the core of the question is why int i {} performs a value initialisation of i, but std::shared_ptr<Foo> sharedFoo {} does a default initialisation of sharedFoo

Well, that's exactly what you asked it to do. Value initialization does different things for scalars (like int) and class objects with constructors.

This is because scalars have a well-defined zero value, whereas class types at most have a well-defined default constructor, which may have to establish some invariants before the object can be used.

If a class has only non-trivial constructors, you can't use an object of that class at all until the constructor has completed because the object formally doesn't exist. There's no reasonable "zero value" for such classes.

and doesn't value initialise the contained pointer.

But it does. It does exactly that. Value initializing a pointer sets it to nullptr. You can't dereference that either.

My intuition had made me think that this would be value initialised, using the default constructor for Foo

But you haven't declared any object of type Foo (let alone initialized it). You declared a (smart) pointer to Foo, and that's what you value-initialized.

You can, if you wish, write a smart pointer class that always creates an object of the contained type. But that isn't what shared_ptr does.

Sign up to request clarification or add additional context in comments.

Comments

2
std::shared_ptr<Foo> sharedFoo{};

sharedFoo is a default constructed, empty, shared_ptr and dereferencing it has undefined behavior. It does not own an object.

It's the same as-if you'd done:

std::shared_ptr<Foo> sharedFoo;
sharedFoo->getValue();

What you want is likely:

auto sharedFoo = std::make_shared<Foo>();

10 Comments

@HarryP2023 It's simply a default constructed (empty) shared_ptr<Foo>, not a shared_ptr<Foo> with a default constructed Foo
@HarryP2023 Yes, the shared_ptr is default-initialized, see: value initialization
Well, value initialization of an object with a default constructor is default initialization (ie, the constructor is used). Value initialization of int or other scalars is zero initialization. It's all listed.
@HarryP2023 now matter how the constructor is invoked, its just a constructor, and the default constructor of the smart pointer does not create a Foo, because thats not what it is meant to be used for. It is meant to create an emtpy smart pointer.
@HarryP2023 • a std::shared_ptr<Foo> models a pointer (a smart pointer, with federated ownership), not a Foo.
|
2

The following two variable definitions

int i {};
std::shared_ptr<Foo> sharedFoo { };

both perform value-initialization.

It is also instructive to compare with:

Foo* p {};

In this case, p is initialized to a null pointer, because value-initialization for a pointer type always initializes it to a null pointer. It is unclear why you would expect value-initialization for a pointer to create an object for the pointer to point to.

Value-initialization of std::shared_ptr<Foo> calls the default constructor, and the result is that the std::shared_ptr<Foo> object is placed in a state that is logically similar to the value-initialized pointer state, i.e., null.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.