Как можно избежать повторения информации о типах, используя функцию вывода типов в c ++ 11 для создания экземпляра shared_ptr? - PullRequest
1 голос
/ 08 октября 2011

Вы можете создать экземпляр пары, используя новое ключевое слово c ++ 11 auto, чтобы полностью минимизировать информацию о типе, которую вы предоставляете компилятору:

auto my_pair(make_pair(10,"yay"));

Есть ли подобный механизм для создания shared_ptr? Единственный синтаксис, который я могу найти, повторяет информацию о типе компилятору 2 раза:

auto p = make_shared<int>(10);
                       \   \
                        \   ` type information part 1
                         `duplicate type information part 2

Что не намного лучше, чем синтаксис pre-c ++ 11

shared_ptr<int> p( new int(10) );

Я думал, что может существовать шаблонная функция make_shared_ptr, как make_pair. Существует ли такой шаблон для shared_ptr?

(Я немного напуган, пытаясь свернуть свое собственное из-за всех проблем безопасности исключений, упомянутых в документах boost. Я также хотел бы избежать создания своего собственного идиотического шаблона.)

Ответы [ 4 ]

5 голосов
/ 08 октября 2011

Вы не.

Тип, переданный в make_shared, ничего не значит; этого недостаточно, чтобы знать, какой тип выделить. Возможно, у вас есть какой-то класс, который вы хотите использовать, который просто получает int в качестве параметра для своего конструктора.

make_shared не существует для упрощения ввода конструкторов shared_ptr. Он существует по двум причинам:

1: Намного сложнее сделать что-то вроде этого:

SomeFunc(shared_ptr<string>( new string("hi") ), shared_ptr<string>( new string("hi2") ));

Из-за того, как C ++ определяет порядок вычисления выражений, возможно, что вы выделите оба строковых объекта перед созданием временных файлов shared_ptr. Если один из них выбрасывает, вы теряете память.

И это ужасно.

Использование make_shared означает, что вы не вызываете new явно. Таким образом, вы не можете получить эту проблему.

2: Позволяет выполнить оптимизацию, когда блок управления shared_ptr выделяется вместе с хранилищем для рассматриваемого объекта. Выполнение shared_ptr<string>( new string("hi") ) выделит 2 блока памяти: один для string и один для внутреннего блока управления для shared_ptr. Вызов make_shared<string>("hi") выделит только один блок: и string, и внутренний блок управления будут распределены вместе.

То, о чем вы говорите, является чем-то большим, чем гипотетический copy_shared, где вам явно требуется ровно один параметр, и вы явно копируете значение. Конечно, вы также хотели бы получить move_shared, который принимает значение на && и использует конструкцию перемещения.

4 голосов
/ 08 октября 2011

В ваших публикациях нет дублирования.make_shared<T> принимает аргументы конструктора , которые вовсе не обязательно соответствуют самому T.Даже ваш пример демонстрирует это - вы получаете shared_ptr<std::string>, но не прошли make_shared a std::string вообще - вы передали его const char(&)[N].Это не игнорируемое преобразование.Это не случается никакой специфической для строки магии.Это происходит потому и только потому, что std::string может принять это в своем конструкторе.Я мог бы написать любое количество тривиальных примеров, чтобы продемонстрировать это.Как насчет вектора?

auto p = make_shared<std::vector<float>>(100); // pass 100 to vector constructor

Если вы не хотите утверждать, что make_shared должен иметь возможность выводить int в качестве аргумента из std::vector<float>, что, очевидно, неверно, потому что std::vector<float> имеет числодругих конструкторов с одним аргументом, не говоря уже о конструкторах с несколькими аргументами, или о том, что он должен иметь возможность выводить std::vector<float> из int, что также очевидно ложно, поскольку любой std::vector<T> может инициализироваться из int, не говоря уже о всехдругие примитивы и UDT, которые могут инициализироваться с int.

2 голосов
/ 08 октября 2011

Я не думаю, что вы должны бояться исключительной безопасности в этом случае. То, что вы рассматриваете, может выглядеть так:

template<
    typename T
    // for convenience:
    typename Decayed = typename std::decay<T>::type
>
std::shared_ptr<Decayed>
deduce_shared(T&& t)
{
    static_assert(
        !std::is_array<
            typename std::remove_reference<T>::type
        >::value
        , "Array parameters not allowed" );

    return std::make_shared<Decayed>(std::forward<T>(t));
}

что довольно безобидно. Даже возвращение std::shared_ptr<Decayed>(new Decayed(std::forward<T>(t))) безопасно, я просто краду возможность оптимизации std::make_shared.

0 голосов
/ 28 ноября 2013

Я хотел бы отметить, что принятый ответ потенциально опасен - он делает то, что обещает, но может дорого вам обойтись - безобидным это не так.

Таким образом, вы всегда пересылка экземпляра того, что вы хотите создать, в конструктор копирования.Это ухудшает производительность, но, возможно, что еще более важно, это может даже укусить вас в спину.

В случае, если переданный объект содержит любые ресурсы, уже инициализированные (на месте, даже) как поля, идеструктор освобождает их, затем shared_ptrs, таким образом, всегда указывает на объект с нет фактическими ресурсами.

Пример

Обратите внимание на это надуманное (немного RAII в неведении, но мыполучить к этому) пример:

#include <memory>
#include <iostream>

using namespace std;

template<
        typename T,
        typename Decayed = typename std::decay<T>::type
>std::shared_ptr<Decayed> deduce_shared(T&& t){
        static_assert(!std::is_array<typename std::remove_reference<T>::type>::value, "Array parameters are not allowed.");
        return std::make_shared<Decayed>(std::forward<T>(t));
}

class Container{
        public:
                int* pricyResource = reinterpret_cast<int*>(0xBAADDEAD);

                Container(int* pricyResource):pricyResource(pricyResource){
                        cout << "Constructing, with nice resource:" << *pricyResource << endl;
                }

                Container(const Container&){
                        cout << "Copy constructing." << endl;
                }

                ~Container(){
                        cout << "Destructing." << endl;
                        delete pricyResource;
                }
};

int* acquireResource(){
        return new int(9001);
}

int main(){
        auto shared = deduce_shared(Container(acquireResource()));
        cerr << shared->pricyResource << endl;
        cerr << "Destruction / potential segfault (this case) after scope ends." << endl;
        return 0;
}

Один потенциальный выход: (Вы не гарантированы, что SIGSEGV.)

Constructing, with nice resource:9001
Copy constructing.
Destructing.
0xbaaddead
Destruction / potential segfault (this case) after scope ends.
Destructing.
Segmentation fault

... это такое большое дело?

Конечно, если ресурс будет помещен в shared_ptr, все будет лучше, но ненамного.Вы также можете утверждать, что ресурс не должен инициализироваться вне класса, но я уверен, что вы можете подумать о ситуации, когда это не имеет особого смысла.

Предполагая, что он защищен shared_ptr, подумайте, что произойдет, еслиесть пул ресурсов пользователей, а ресурс поддерживается живым с помощью shared_ptrs участников?Что ж, в случае, если нет членов (нет запросов клиентов, нет записей в очереди сообщений и т. Д.), И один элемент появляется в пуле, дорогой ресурс мигает и исчезает - конструктор первого члена создаетресурс, затем его деструктор немедленно удаляет его.Если его конструктор копирования ожидает, что ресурс будет там, или не хочет этого делать, и фактически проверяет, существует ли он, и замечает, что он не существует, ресурс создается / подключается / снова выделяется.

(Можно утверждать, что где-то должен быть уникальный указатель и выделенный «владелец» этого ресурса, и вы правы - но давайте предположим иначе.)

Заключение

В общем, не делайте этого - просто нет причин для этого.Используйте make_shared<Type(constructor,arguments,of,the,type).

Мелкий шрифт : конечно, вы можете положиться на конструктор по умолчанию, который ничего не делает (или переключает флаги), и деструктор, реализующий такое пустое состояние, но при этомсомнительный дизайн.

Чрезвычайно мелкий шрифт : Мне известна политика SO в отношении ответов на ответы с ответами, но у меня просто недостаточно представителя, чтобы комментировать или редактировать этот ответ,и думал, что я мог бы спасти некоторых людей некоторое время.Я также понимаю, что другие люди возражали против использования намерения ОП, но не видели ничего против этого конкретного фрагмента.

...