Std::Shared_Ptr and Initializer Lists

std::shared_ptr and initializer lists

Try this:

auto ptr = std::make_shared<Func>(std::initializer_list<std::string>{"foo", "bar", "baz"});

Clang is not willing to deduce the type of {"foo", "bar", "baz"}. I'm currently not sure whether that is the way the language is supposed to work, or if we're looking at a compiler bug.

std::initializer_list and std::make_shared: too many arguments ... 3 expected 0 provided

{1,2,3} can be multiple things, and make_shared has no possibility of knowing what it is at the time parameter pack is expanded.
If you don't want to state the long std::initializer_list<int>{1,2,3} explicitly, the easiest solutions would be:
a. shortening the type's name: using ints=std::initializer_list<int>;
b. wrapping the call:

auto make_shared_S(int x, double y, std::initializer_list<int> l)
{
return std::make_shared<S>(x, y, l);
}

Demo: https://godbolt.org/z/WErz87Ks4

Casting shared_ptr in initializer list

A shared_ptr<Derived> can be converted to a shared_ptr<Base>, but it is not a shared_ptr<Base>.

In other words, this compiles:

std::shared_ptr<Derived> pd;
std::shared_ptr<Base> pb = pd;

But this does not:

std::shared_ptr<Base>& pb = pd;

But this does, because const references can bind to temporaries and this will implicitly perform a conversion:

std::shared_ptr<Base> const& pb = pd;

The problem is that you're taking non-const lvalue references in your constructors. You should either take const lvalue references or just take them by value.

How to initialize a shared pointer in the initialization list of a constructor?

Add a constructor explicit Bar::Bar(const callback&). explicit will prevent mistakes related to automatic conversion. Then you can initialize a shared_ptr<Bar> like this:

Foo::Foo(const callback& cb)
: m_ptr(std::make_shared<Bar>(cb))

See documentation for make_shared here.

std::make_shared with std::initializer_list

For this to work, you need to create a custom make_shared_from_list, as make_shared does not support non-explicit initializer lists. The reason is described well by @brian.

I would use a traits class to map a type T to the type of initializer list.

template<class>struct list_init{};// sfinae support
template<> struct list_init<Derived>{using type=std::pair<int, std::shared_ptr<Base>>;};

template<class T>using list_init_t=typename list_init<T>::type;

template<class T>
std::shared_ptr<T> make_shared_from_list( std::initializer_list<list_init_t<T>> list ){
return std::make_shared<T>( std::move(list) );
}

or something like that.

Alternatively, "cast" the {...} to the initializer_list<blah> directly (not a cast, but rather a construction) may work.

In theory, sufficient reflection metaprogramming support would allow shared_ptr to do this without the traits class, bit that is pretty far down the pipe.

Shared argument in constructor initializer list

Actually, I thought of a better solution. Create the shared_ptr, then delegate to a private constructor that will use it to construct _a and _b. (Credit to Zan Lynx for suggesting the use of a function parameter as temporary storage.)

class Foo {
public:
Foo(): Foo(std::make_shared<SomeClass>()) {}
private:
Foo(std::shared_ptr<SomeClass> ptr): _a(ptr), _b(ptr) {}
A _a;
B _b;
};

Conditionally make shared_ptr in initializer list null

b(true ? std::make_shared<Bar>() : nullptr)

shared pointer in constructor initializer list

The segmentation fault for accessing a unassigned shared_ptr<> is perfectly normal, as you are essentially dereferencing null.

The interesting bit is why your first test did not crash.

int_ptr my_int_ptr();

This line of code does not do what you think it does. You are forward declaring a function called my_int_ptr that returns an int_ptr, not declaring a default-initialized instance of int_ptr.



Related Topics



Leave a reply



Submit