Std:: shared_ptr и списки инициализаторов

Конструктор std:: shared_ptr не ведет себя так, как я ожидал:

#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;
}

Я делаю что-то неправильно или компилятор? Компилятор:

$clang++ --version Apple clang version 4.0 (теги/Apple/clang-421.0.57) (на основе LLVM 3.1svn)

edit: shared_ptr вместо make_shared.

Здесь ошибка:

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.

Ответы

Ответ 1

Попробуйте следующее:

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

Clang не хочет выводить тип {"foo", "bar", "baz"}. В настоящее время я не уверен, работает ли этот язык, или если мы смотрим на ошибку компилятора.

Ответ 2

Конструктор shared_ptr<T> принимает в качестве аргумента указатель типа T*, предположительно указывающий на динамически выделенный ресурс (или, по крайней мере, то, что может быть освобождено от делетера). С другой стороны, make_shared делает конструкцию для вас и принимает аргументы конструктора напрямую.

Итак, либо вы это скажете:

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

Или, намного лучше и эффективнее:

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

Последняя форма заботится о распределении и построении для вас, а в процессе создает более эффективную реализацию.

Конечно, вы могли бы также сказать make_shared<Foo>(Foo('a', true, Blue)), но это просто создало бы ненужную копию (которая может быть отменена), и, что более важно, она создает ненужную избыточность. [Edit] Для инициализации вашего вектора это может быть лучшим способом:

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

Важным моментом является то, что make_shared выполняет динамическое распределение для вас, в то время как конструктор shared-ptr не делает этого и вместо этого становится владельцем.

Ответ 3

Вам нужно использовать make_shared, если вы хотите создать новый объект, построенный из этих аргументов, на который указывает shared_ptr. shared_ptr<T> как указатель на T - его нужно построить с указателем на T, а не с T.

Изменить: идеальная переадресация на самом деле не идеальна, когда задействованы списки инициализаторов (это сосание). Это не ошибка в компиляторе. Вам нужно будет создать rvalue типа Func вручную.