У меня есть этот код, который не работает, но я думаю, что цель ясна:
testmakeshared.cpp
#include <memory>
class A {
public:
static ::std::shared_ptr<A> create() {
return ::std::make_shared<A>();
}
protected:
A() {}
A(const A &) = delete;
const A &operator =(const A &) = delete;
};
::std::shared_ptr<A> foo()
{
return A::create();
}
Но я получаю эту ошибку при компиляции:
g++ -std=c++0x -march=native -mtune=native -O3 -Wall testmakeshared.cpp
In file included from /usr/lib/gcc/x86_64-redhat-linux/4.6.1/../../../../include/c++/4.6.1/bits/shared_ptr.h:52:0,
from /usr/lib/gcc/x86_64-redhat-linux/4.6.1/../../../../include/c++/4.6.1/memory:86,
from testmakeshared.cpp:1:
testmakeshared.cpp: In constructor ‘std::_Sp_counted_ptr_inplace<_Tp, _Alloc, _Lp>::_Sp_counted_ptr_inplace(_Alloc) [with _Tp = A, _Alloc = std::allocator<A>, __gnu_cxx::_Lock_policy _Lp = (__gnu_cxx::_Lock_policy)2u]’:
/usr/lib/gcc/x86_64-redhat-linux/4.6.1/../../../../include/c++/4.6.1/bits/shared_ptr_base.h:518:8: instantiated from ‘std::__shared_count<_Lp>::__shared_count(std::_Sp_make_shared_tag, _Tp*, const _Alloc&, _Args&& ...) [with _Tp = A, _Alloc = std::allocator<A>, _Args = {}, __gnu_cxx::_Lock_policy _Lp = (__gnu_cxx::_Lock_policy)2u]’
/usr/lib/gcc/x86_64-redhat-linux/4.6.1/../../../../include/c++/4.6.1/bits/shared_ptr_base.h:986:35: instantiated from ‘std::__shared_ptr<_Tp, _Lp>::__shared_ptr(std::_Sp_make_shared_tag, const _Alloc&, _Args&& ...) [with _Alloc = std::allocator<A>, _Args = {}, _Tp = A, __gnu_cxx::_Lock_policy _Lp = (__gnu_cxx::_Lock_policy)2u]’
/usr/lib/gcc/x86_64-redhat-linux/4.6.1/../../../../include/c++/4.6.1/bits/shared_ptr.h:313:64: instantiated from ‘std::shared_ptr<_Tp>::shared_ptr(std::_Sp_make_shared_tag, const _Alloc&, _Args&& ...) [with _Alloc = std::allocator<A>, _Args = {}, _Tp = A]’
/usr/lib/gcc/x86_64-redhat-linux/4.6.1/../../../../include/c++/4.6.1/bits/shared_ptr.h:531:39: instantiated from ‘std::shared_ptr<_Tp> std::allocate_shared(const _Alloc&, _Args&& ...) [with _Tp = A, _Alloc = std::allocator<A>, _Args = {}]’
/usr/lib/gcc/x86_64-redhat-linux/4.6.1/../../../../include/c++/4.6.1/bits/shared_ptr.h:547:42: instantiated from ‘std::shared_ptr<_Tp1> std::make_shared(_Args&& ...) [with _Tp = A, _Args = {}]’
testmakeshared.cpp:6:40: instantiated from here
testmakeshared.cpp:10:8: error: ‘A::A()’ is protected
/usr/lib/gcc/x86_64-redhat-linux/4.6.1/../../../../include/c++/4.6.1/bits/shared_ptr_base.h:400:2: error: within this context
Compilation exited abnormally with code 1 at Tue Nov 15 07:32:58
Это сообщение в основном говорит о том, что какой-то случайный метод, находящийся в стеке экземпляров шаблона, ::std::make_shared
не может получить доступ к конструктору, потому что он защищен.
Но я действительно хочу использовать и то, ::std::make_shared
и другое, чтобы никто не смог создать объект этого класса, на который не указывает объект ::std::shared_ptr
. Есть ли способ сделать это?
c++
c++11
shared-ptr
всевозможный
источник
источник
Ответы:
Этот ответ , вероятно, лучше, и тот, который я, скорее всего, приму. Но я также придумал метод, который более уродлив, но все же позволяет все по-прежнему быть встроенным и не требует производного класса:
Редактировать 2017-01-06: я изменил это, чтобы было ясно, что эта идея понятна и просто расширяема для конструкторов, которые принимают аргументы, потому что другие люди давали ответы в том же духе и, казалось, были смущены этим.
источник
protected
вместоprivate
. И под «этим» я имею в видуthis_is_private
класс, который, возможно, следует переименовать в таком случае. Я обычно называю этоconstructor_access
в своем коде.{}
к закрытому тегу, не имея доступа к имени типа (протестировано с g ++ 4.9.0). Без реальных параметров он пытается построитьA
из {}, хотя я понятия не имею, почему, и терпит неудачу. Я думаю, что сделав конструктор this_is_private закрытым и предоставив статический метод для его создания, это исправит его, так как не должно быть никакого доступа к этому методу извне, если вы не пропустите тип в сигнатуре функции-члена.this_is_private
приватный ctor, ты можешь сделать класс A другом. Кажется, чтобы закрыть лазейку.Рассматривая требования для
std::make_shared
20.7.2.2.6 создания shared_ptr [util.smartptr.shared.create], пункт 1:Поскольку требование безоговорочно определено в терминах этого выражения, а такие вещи, как сфера действия, не принимаются во внимание, я думаю, что такие уловки, как дружба, уместны.
Простое решение состоит в том, чтобы извлечь из
A
. Это не требует созданияA
интерфейса или даже полиморфного типа.источник
shared_ptr
во время создания экземпляра хранитель удаляется, и если вы используетеmake_shared
средство удаления, оно обязательно должно использовать правильный тип.Возможно, самое простое решение. Основано на предыдущем ответе Мохит Арон и включает предложение ДЛФ.
источник
A
есть конструкторы не по умолчанию , вам необходимо будет также подвергать их:struct make_shared_enabler : public A { template <typename... Args> make_shared_enabler(Args &&... args):A(std::forward<Args>(args)...) {} };
. Это делает все частные конструкторыA
видимыми какmake_shared_enabler
конструкторы. Использование функции наследования конструкторов (using A::A;
) кажется здесь не помогает, потому что конструкторы все еще будут приватными.class A { ... private: struct A_shared_enabler; }; class A::A_shared_enabler : public A { ... }
. Смотрите здесь cpp.sh/65qbr .Вот изящное решение для этого:
источник
MakeSharedEnabler
локально внутриA::Create()
.Как насчет этого?
источник
::std::make_shared
имеет функциональность сверх того, что просто делает shared_ptr чем-то. Он распределяет счетчик ссылок вместе с объектом, чтобы они находились близко друг к другу. Я действительно очень хочу использовать::std::make_shared
.источник
Поскольку мне не понравились уже предоставленные ответы, я решил поискать и найти решение, которое не является таким общим, как предыдущие ответы, но мне оно нравится больше (тм). Оглядываясь назад, он не намного лучше, чем тот, который предлагает Омнифарий, но могут быть и другие люди, которым это тоже нравится :)
Это не придумано мной, но это идея Джонатана Уэйкли (разработчика GCC).
К сожалению, он работает не со всеми компиляторами, поскольку полагается на небольшое изменение в реализации std :: allocate_shared. Но теперь это изменение является предлагаемым обновлением для стандартных библиотек, поэтому оно может быть поддержано всеми компиляторами в будущем. Работает на GCC 4.7.
C ++ стандартный запрос на изменение рабочей группы библиотеки находится здесь: http://lwg.github.com/issues/lwg-active.html#2070
Патч GCC с примером использования здесь: http://old.nabble.com/Re%3A--v3--Implement-pointer_traits-and-allocator_traits-p31723738.html
Решение работает на идее использовать std :: allocate_shared (вместо std :: make_shared) с пользовательским распределителем, который объявлен другом для класса с закрытым конструктором.
Пример из OP будет выглядеть так:
Более сложный пример, основанный на утилите, над которой я работаю. С этим я не мог использовать решение Люка. Но тот, который Omnifarius может быть адаптирован. Не то чтобы хотя в предыдущем примере каждый мог создать объект A, используя MyAlloc, в этом примере нет способа создать A или B, кроме метода create ().
источник
В идеале, я думаю, что для идеального решения потребуются дополнения к стандарту C ++. Эндрю Шеплер предлагает следующее:
(Иди сюда для всей темы)
использование
Если / когда вышеупомянутое добавлено к стандарту, мы просто сделаем:
Если это также звучит как важное дополнение к стандарту для вас, не стесняйтесь добавлять свои 2 цента в связанную группу isocpp Google.
источник
Я понимаю, что этот поток довольно старый, но я нашел ответ, который не требует наследования или дополнительных аргументов для конструктора, который я не мог увидеть в другом месте. Это не портативно, хотя:
Я тестировал на Windows и Linux, может потребоваться настройка для разных платформ.
источник
std::shared_ptr_access
в стандарт, которое можно рассматривать как позволяющее сделать все вышеперечисленное простым и переносимым способом.Существует более сложная и интересная проблема, которая возникает, когда у вас есть два строго связанных класса A и B, которые работают вместе.
Скажи «А» - «мастер-класс», а «Б» - «Раб». Если вы хотите ограничить создание экземпляра B только для A, вы сделаете конструктор B личным, а друга B - таким, как этот
К сожалению, вызов
std::make_shared<B>()
из методаA
заставит компилятор жаловатьсяB::B()
на приватность.Мое решение этого состоит в том, чтобы создать открытый
Pass
фиктивный класс (такой жеnullptr_t
) внутри,B
который имеет приватный конструктор, с которым он дружит,A
и делаетB
конструктор публичным и добавляетPass
к его аргументам, вот так.источник
Если вы также хотите включить конструктор, который принимает аргументы, это может немного помочь.
источник
[Изменить] Я прочитал ветку, отмеченную выше, по стандартизированному
std::shared_ptr_access<>
предложению. Внутри был ответ, отмечающий исправлениеstd::allocate_shared<>
и пример его использования. Я адаптировал его к заводскому шаблону ниже и протестировал его в gcc C ++ 11/14/17. Это также работаетstd::enable_shared_from_this<>
, поэтому, очевидно, было бы предпочтительнее моего первоначального решения в этом ответе. Вот...[Orig] Я нашел решение, используя конструктор псевдонимов общего указателя. Это позволяет ctor и dtor быть приватными, а также использовать финальный спецификатор.
Обратите внимание, что подход, описанный выше, не очень хорошо работает,
std::enable_shared_from_this<>
потому что инициалstd::shared_ptr<>
относится к оболочке, а не к самому типу. Мы можем обратиться к этому с эквивалентным классом, который совместим с фабрикой ...Наконец, кто-то сказал, что clang жаловался на то, что Factory :: Type является приватным при использовании в качестве друга, поэтому просто сделайте его публичным, если это так. Разоблачение это не вредит.
источник
У меня была та же проблема, но ни один из существующих ответов не был действительно удовлетворительным, так как мне нужно передать аргументы защищенному конструктору. Более того, мне нужно сделать это для нескольких классов, каждый из которых принимает разные аргументы.
С этой целью, основываясь на нескольких существующих ответах, в которых все используют похожие методы, я представляю этот маленький самородок:
источник
Корень проблемы в том, что если функция или класс вашего друга делает вызовы вашего конструктора более низкого уровня, они также должны быть дружественными. std :: make_shared - это не та функция, которая на самом деле вызывает ваш конструктор, так что это не имеет значения.
std :: _ Ref_count_obj на самом деле вызывает ваш конструктор, поэтому он должен быть другом. Поскольку это немного неясно, я использую макрос
Тогда объявление вашего класса выглядит довольно просто. Вы можете сделать один макрос для объявления ptr и класса, если хотите.
Это на самом деле важная проблема. Чтобы сделать поддерживаемый переносимый код, вам нужно скрыть как можно большую часть реализации.
скрывает, как вы обрабатываете свой умный указатель, вы должны быть уверены, что используете ваш typedef. Но если вам всегда нужно создавать его с помощью make_shared, это побеждает цель.
Приведенный выше пример вынуждает код, использующий ваш класс, использовать ваш конструктор умных указателей, что означает, что если вы переключитесь на новый вид умного указателя, вы измените объявление класса и у вас будет хороший шанс быть завершенным. НЕ предполагайте, что ваш следующий босс или проект будут использовать план stl, boost и т. Д., Чтобы когда-нибудь изменить его.
Делая это в течение почти 30 лет, я заплатил большую цену во времени, боли и побочных эффектах, чтобы исправить это, когда это было сделано неправильно много лет назад.
источник
std::_Ref_count_obj
это деталь реализации. Это означает, что пока это решение может работать для вас, на данный момент, на вашей платформе. Но он может не работать для других и может перестать работать в любое время, когда ваш компилятор обновляется, или, может быть, даже если вы просто измените флаги компиляции.Вы можете использовать это:
источник
std::make_shared
.источник