повысить make_shared без аргумента шаблона

Я пытаюсь передать указатель на переменную стека функции (я не контролирую), которая принимает только boost::shared_ptr.

Согласно этот ответ, использование boost::make_shared - это путь. Чтобы проверить эту функциональность, я написал это:

#include <iostream>
#include <boost/shared_ptr.hpp>
#include <boost/make_shared.hpp>

int main(int argc, char const *argv[])
{
    int i = 10;
    boost::shared_ptr<int> int_ptr = boost::make_shared(i); // doesn't work
    some_function(int_ptr); // this function takes only shared_ptr
    return 0;
}

Но выдает следующую ошибку:

error: no matching function for call to ‘make_shared(int&)’
boost::shared_ptr<int> int_ptr = boost::make_shared(i);
                                                     ^

Если я добавлю аргумент шаблона, он будет работать, но в чем причина этого?

boost::shared_ptr<int> int_ptr = boost::make_shared<int>(i);

Благодарю вас!


person simplename    schedule 17.03.2017    source источник
comment
Спросите себя, какой тип общего указателя должна использовать функция, если вы просто дадите ей int. Откуда он знает, что вам нужен shared_ptr<int>, а не shared_ptr<vector<int>>, который также может быть построен с помощью int.   -  person NathanOliver    schedule 17.03.2017
comment
Обратите внимание, что вывод аргумента шаблона не может включать информацию о типе, которому будет присвоен результат. Часть boost::shared_ptr<int> int_ptr = нельзя учитывать при определении соответствующих аргументов шаблона.   -  person François Andrieux    schedule 17.03.2017
comment
Создание shared_ptr, указывающего на выделенную в стеке память, будет иметь катастрофические последствия, когда он попытается удалить эту память. Нужно ли изменять i с помощью указателя или будет достаточно копии i?   -  person chris    schedule 17.03.2017
comment
@chris i не нужно изменять, и копии будет достаточно, но я подумал, что в этом весь смысл использования make_shared. Поскольку функция, которой я должен отправить int_ptr, не принимает ничего, кроме shared_ptr, у меня нет другого выбора.   -  person simplename    schedule 17.03.2017
comment
@simplename, Ну, в вопросе указан указатель на переменную стека. Это не обязательно пункт make_shared. Он избегает new в вашем коде, оптимизирует динамическое размещение и предотвращает возможность утечки объекта из-за исключений в таких случаях, как f(new A, new A).   -  person chris    schedule 17.03.2017


Ответы (2)


Учитывая шаблон boost::make_shared<T>:

namespace boost {
    template<typename T, typename Arg1>
      shared_ptr<T> make_shared( Arg1 const & arg1 );
}

Механизм шаблона может вывести тип параметра arg1. Потому что он «видит» тип аргумента i (то есть int). Однако он не может определить возвращаемый тип T. Он не знает тип T boost::shared_ptr<T>, которому вы назначите (т. е. он не имеет возможности узнать тип int_ptr).

boost::shared_ptr<T> использует разные типы для аргумента (Arg1) и возврата (T), чтобы вы могли создавать общие указатели из аргументов, отличных от типа указателя. Например, от double до int:

double d = 10.0;
std::shared_ptr<int> int_ptr = std::make_shared<int>(d);

Если вы хотите создать общие указатели, тип которых совпадает с типом аргумента, вы можете написать обертку:

template<typename T>
boost::shared_ptr<T> my_make_shared(T const & arg) {
    return boost::make_shared<T>(arg);
}

Но имейте в виду, что пока это работает:

int i = 10.0;
std::shared_ptr<int> int_ptr = my_make_shared(i); // OK

Неявное преобразование типа не:

double d = 10.0;
std::shared_ptr<int> int_ptr = my_make_shared(d); // ERROR

Надеюсь, поможет!

person Guilherme Ferreira    schedule 17.03.2017
comment
Что значит иметь shared_ptr<int>, указывающий на double? - person simplename; 22.03.2017
comment
На самом деле у вас нет shared_ptr<int>, указывающего на double. Но после того, как make_shared<int> выделяет память для int, он инициализирует память значением double. - person Guilherme Ferreira; 23.03.2017

Хотя ответ Guilherme Ferreira подробно описывает вывод аргумента шаблона (и является правильным в этом отношении), я считаю, что это не тот ответ, который вы ищете.

Я пытаюсь передать указатель на переменную стека функции (я не контролирую), которая принимает только boost::shared_ptr.

shared_ptr означает совместное владение указанным объектом. Если функция, которую вы пытаетесь вызвать, сохраняет указатель в какой-либо из своих структур данных, например, в контейнере, а затем возвращается, указатель станет висящим, как только значение в стеке будет уничтожено, несмотря на то, что shared_ptr все еще сохраняется. ссылка. Чтобы сделать то, что вы хотите, вы должны быть абсолютно уверены, что функция нигде не сохраняет указатель и может использовать его только во время этого одного вызова.

При выполнении этого условия можно создать shared_ptr, указывающий на значение в стеке, но нельзя использовать для этого make_shared. make_shared выделяет новый объект в куче вместе со счетчиком ссылок для него и инициализирует его аргументами, которые вы передали вызову функции. Возвращенный shared_ptr указывает на этот новый объект, а не на объект в стеке.

void foo()
{
    int n = 10;
    boost::shared_ptr< int > pn = boost::make_shared< int >(n);

    assert(*pn == 10); // succeeds
    assert(pn.get() == &n); // fails

    bar(pn);
}

Это означает, что изменения, которые bar вносит в указанный int, не отражаются на n.

Чтобы создать shared_ptr для существующего объекта, вы должны напрямую использовать его конструктор. Кроме того, поскольку время жизни объекта контролируется стеком, вы должны запретить shared_ptr уничтожать объект. Это можно сделать, указав недействующее средство удаления в конструкции shared_ptr.

void foo()
{
    int n = 10;
    boost::shared_ptr< int > pn(&n, boost::null_deleter());

    assert(*pn == 10); // succeeds
    assert(pn.get() == &n); // succeeds

    bar(pn);
}

Обратите внимание, однако, что этот код по-прежнему выделяет память кучи для использования счетчика ссылок shared_ptr, поэтому вы не выигрываете в производительности.

person Andrey Semashev    schedule 20.03.2017