std::shared_ptr and initializer lists
Asked Answered
P

3

31

The std::shared_ptr constructor isn't behaving as I expected:

#include <iostream>
#include <vector>

void func(std::vector<std::string> strings)
{
    for (auto const& string : strings)
    {
        std::cout << string << '\n';
    }
}

struct Func
{
    Func(std::vector<std::string> strings)
    {
        for (auto& string : strings)
        {
            std::cout << string << '\n';
        }
    }
};

int main(int argc, const char * argv[])
{

    func({"foo", "bar", "baz"});
    Func({"foo", "bar", "baz"});
    //auto ptr = std::make_shared<Func>({"foo", "bar", "baz"}); // won't compile.
    //auto ptr = std::make_shared<Func>{"foo", "bar", "baz"}; // nor this.
    return 0;
}

Am I doing something wrong or is the compiler? The compiler is:

$ clang++ --version Apple clang version 4.0 (tags/Apple/clang-421.0.57) (based on LLVM 3.1svn)

edit: shared_ptr instead of make_shared.

Here's the error:

make -k 
clang++ -std=c++11 -stdlib=libc++    main.cc   -o main
main.cc:28:18: error: no matching function for call to 'make_shared'
      auto ptr = std::make_shared<Func>({"foo", "bar", "baz"});
                 ^~~~~~~~~~~~~~~~~~~~~~
/usr/bin/../lib/c++/v1/memory:4621:1: note: candidate function not viable:
     requires 0 arguments, but 1 was provided
make_shared(_Args&& ...__args)
^
1 error generated.
Phallic answered 5/8, 2012 at 23:27 Comment(0)
L
37

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.

Laughter answered 6/8, 2012 at 0:0 Comment(3)
Last I heard, it's known that perfect forwarding is not actually perfect at all when initializer lists are concerned.Peursem
{"foo", "bar", "baz"} is not an expression and as such has no type (except when used with auto).. Although it would be niceIsm
{"foo", "bar", "baz"} can be array of std::string, char [][], wchar_t [][].... Compiler must know exactly what it needs to create.Betteann
P
5

The constructor of shared_ptr<T> takes a pointer of type T* as its argument, assumed to point to a dynamically allocated resource (or at least something that can be freed by the deleter). On the other hand, make_shared does the construction for you and takes the constructor arguments directly.

So either you say this:

std::shared_ptr<Foo> p(new Foo('a', true, Blue));

Or, much better and more efficiently:

auto p = std::make_shared<Foo>('a', true, Blue);

The latter form takes care of the allocation and construction for you, and in the process creates a more efficient implementation.

You could of course also say make_shared<Foo>(Foo('a', true, Blue)), but that would just create an unnecessary copy (which may be elided), and more importantly it creates needless redundancy. [Edit] For initializing your vector, this may be the best method:

auto p = std::make_shared<Func>(std::vector<std::string>({"a", "b", "c"}));

The important point is, though, that make_shared performs the dynamic allocation for you, while the shared-ptr constructor does not, and instead takes ownership.

Potter answered 5/8, 2012 at 23:33 Comment(2)
"but that would just create an unnecessary copy" An unnecessary move.Dioptometer
@ildjarn: Well, that all depends, doesn't it? Anyway, "move" is just an optimized copy :-)Potter
P
3

You need to use make_shared if you want to create a new object, constructed from those arguments, pointed to by a shared_ptr. shared_ptr<T> is like a pointer to T- it needs to be constructed with a pointer to T, not a T.

Edit: Perfect forwarding is in fact not at all perfect when initializer lists are involved (which is the suck). It's not a bug in the compiler. You will have to create an rvalue of type Func manually.

Peursem answered 5/8, 2012 at 23:29 Comment(1)
Sorry, the problem was with make_shared. I changed it to shared_ptr whilst futzing.Phallic

© 2022 - 2024 — McMap. All rights reserved.