Saya memiliki kode ini yang tidak berfungsi, tetapi saya pikir maksudnya jelas:
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();
}
Tapi saya mendapatkan kesalahan ini ketika saya kompilasi:
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
Pesan ini pada dasarnya mengatakan bahwa beberapa metode acak turun di tumpukan contoh template dari ::std::make_shared
tidak dapat mengakses konstruktor karena itu dilindungi.
Tapi saya benar-benar ingin menggunakan keduanya ::std::make_shared
dan mencegah siapa pun membuat objek kelas ini yang tidak diarahkan oleh a ::std::shared_ptr
. Apakah ada cara untuk mencapai ini?
c++
c++11
shared-ptr
Beraneka ragam
sumber
sumber
Jawaban:
Jawaban ini mungkin lebih baik, dan yang kemungkinan besar akan saya terima. Tapi saya juga datang dengan metode yang lebih buruk, tetapi masih membiarkan semuanya masih sejajar dan tidak memerlukan kelas turunan:
Sunting 2017-01-06: Saya mengubah ini untuk memperjelas bahwa ide ini jelas dan sederhana untuk konstruktor yang mengambil argumen karena orang lain memberikan jawaban di sepanjang garis itu dan tampak bingung tentang hal ini.
sumber
protected
bukanprivate
. Dan dengan "itu", saya merujuk kethis_is_private
kelas, yang mungkin harus diganti namanya dalam kasus seperti itu. Saya biasanya menyebutnyaconstructor_access
dalam kode saya.{}
tag pribadi tanpa memiliki akses ke nama jenis (diuji dengan g ++ 4.9.0). Tanpa parameter nyata, ia mencoba membangunA
dari {}, meskipun saya tidak tahu mengapa, dan gagal. Saya pikir menjadikan konstruktor this_is_private ini pribadi dan menyediakan metode statis untuk membuatnya memperbaikinya, karena seharusnya tidak ada cara untuk mengakses metode ini dari luar kecuali jika Anda membocorkan jenis tanda tangan fungsi anggota.this_is_private
kartu pribadi kamu bisa membuat teman kelas A. Tampaknya menutup celah itu.Melihat persyaratan untuk
std::make_shared
dalam penciptaan 20.7.2.2.6 shared_ptr [util.smartptr.shared.create], paragraf 1:Karena persyaratannya ditentukan tanpa syarat dalam hal ekspresi itu dan hal-hal seperti ruang lingkup tidak diperhitungkan, saya pikir trik seperti persahabatan benar-benar keluar.
Solusi sederhana untuk diturunkan
A
. Ini tidak perlu mengharuskan membuatA
antarmuka atau bahkan tipe polimorfik.sumber
shared_ptr
menyimpan deleter pada saat instantiation, dan jika Anda menggunakanmake_shared
deleter harus menggunakan tipe yang tepat.Mungkin solusi paling sederhana. Berdasarkan jawaban sebelumnya oleh Mohit Aron dan memasukkan saran dlf.
sumber
A
memiliki konstruktor non-default Anda juga akan perlu untuk mengekspos mereka:struct make_shared_enabler : public A { template <typename... Args> make_shared_enabler(Args &&... args):A(std::forward<Args>(args)...) {} };
. Ini membuat semua konstruktor pribadiA
terlihat sebagaimake_shared_enabler
konstruktor. Menggunakan fitur pewarisan konstruktor (using A::A;
) tampaknya tidak membantu di sini karena konstruktor akan tetap pribadi.class A { ... private: struct A_shared_enabler; }; class A::A_shared_enabler : public A { ... }
. Lihat di sini cpp.sh/65qbr .Inilah solusi yang rapi untuk ini:
sumber
MakeSharedEnabler
secara lokal di dalamnyaA::Create()
.Bagaimana dengan ini?
sumber
::std::make_shared
memiliki fungsionalitas di atas dan di luar hanya membuat shared_ptr untuk sesuatu. Ini mengalokasikan jumlah referensi bersama dengan objek sehingga mereka berada dekat satu sama lain. Saya benar-benar ingin menggunakannya::std::make_shared
.sumber
Karena saya tidak suka jawaban yang sudah disediakan, saya memutuskan untuk mencari dan menemukan solusi yang tidak generik seperti jawaban sebelumnya tetapi saya lebih menyukainya (tm). Kalau dipikir-pikir itu tidak jauh lebih bagus daripada yang disediakan oleh Omnifarius tetapi mungkin ada orang lain yang menyukainya :)
Ini tidak ditemukan oleh saya, tetapi itu adalah ide Jonathan Wakely (pengembang GCC).
Sayangnya itu tidak bekerja dengan semua kompiler karena bergantung pada perubahan kecil dalam implementasi std :: alokasiate_share. Tetapi perubahan ini sekarang merupakan pembaruan yang diusulkan untuk pustaka standar, sehingga mungkin didukung oleh semua kompiler di masa depan. Ini bekerja pada GCC 4.7.
Permintaan perubahan Kelompok Kerja Perpustakaan C ++ standar ada di sini: http://lwg.github.com/issues/lwg-active.html#2070
Patch GCC dengan contoh penggunaan ada di sini: http://old.nabble.com/Re%3A--v3--Implement-pointer_traits-and-allocator_traits-p31723738.html
Solusinya bekerja pada ide untuk menggunakan std :: dialokasikanate_share (bukan std :: make_share) dengan pengalokasi kustom yang dideklarasikan teman ke kelas dengan konstruktor pribadi.
Contoh dari OP akan terlihat seperti ini:
Contoh yang lebih kompleks yang didasarkan pada utilitas yang sedang saya kerjakan. Dengan ini saya tidak bisa menggunakan solusi Luc. Tapi yang oleh Omnifarius bisa diadaptasi. Bukan berarti sementara dalam contoh sebelumnya semua orang dapat membuat objek A menggunakan MyAlloc di sini tidak ada cara untuk membuat A atau B selain metode create ().
sumber
Idealnya, saya pikir solusi sempurna akan membutuhkan penambahan standar C ++. Andrew Schepler mengusulkan yang berikut:
(Buka di sini untuk seluruh utas)
Pemakaian
Jika / ketika hal di atas ditambahkan ke standar, kita cukup melakukan:
Jika ini juga kedengarannya merupakan tambahan penting untuk standar Anda, jangan ragu untuk menambahkan 2 sen Anda ke Grup Google yang terhubung.
sumber
Saya menyadari bahwa utas ini agak lama, tetapi saya menemukan jawaban yang tidak memerlukan warisan atau argumen tambahan kepada konstruktor yang tidak dapat saya lihat di tempat lain. Itu tidak portabel:
Saya telah menguji di windows dan linux, mungkin perlu mengubah untuk platform yang berbeda.
sumber
std::shared_ptr_access
ke standar, yang dapat dilihat memungkinkan untuk melakukan hal di atas dengan cara yang sederhana dan portabel.Ada masalah yang lebih berbulu dan menarik yang terjadi ketika Anda memiliki dua kelas A dan B yang saling terkait yang bekerja sama.
Say A adalah "kelas master" dan B "budaknya". Jika Anda ingin membatasi instantiasi B hanya menjadi A, Anda akan membuat konstruktor B menjadi pribadi, dan teman B ke A menyukai ini
Sayangnya memanggil
std::make_shared<B>()
dari metodeA
akan membuat kompiler mengeluh tentangB::B()
menjadi pribadi.Solusi saya untuk ini adalah membuat
Pass
kelas boneka publik (sepertinullptr_t
) di dalamB
yang memiliki konstruktor pribadi dan berteman denganA
dan membuatB
konstruktor publik dan menambahPass
argumennya, seperti ini.sumber
Jika Anda juga ingin mengaktifkan konstuktor yang mengambil argumen, ini mungkin sedikit membantu.
sumber
[Sunting] Saya membaca utas yang disebutkan di atas pada
std::shared_ptr_access<>
proposal standar . Di dalamnya ada tanggapan mencatat perbaikanstd::allocate_shared<>
dan contoh penggunaannya. Saya telah mengadaptasinya ke templat pabrik di bawah ini, dan mengujinya di bawah gcc C ++ 11/14/17. Ini bekerja denganstd::enable_shared_from_this<>
baik, jadi jelas akan lebih baik daripada solusi asli saya dalam jawaban ini. Ini dia...[Orig] Saya menemukan solusi menggunakan konstruktor aliasing pointer bersama. Hal ini memungkinkan ctor dan dtor untuk menjadi pribadi, serta penggunaan specifier akhir.
Perhatikan bahwa pendekatan di atas tidak cocok
std::enable_shared_from_this<>
karena inisialstd::shared_ptr<>
adalah untuk pembungkus dan bukan tipe itu sendiri. Kami dapat mengatasi ini dengan kelas setara yang kompatibel dengan pabrik ...Terakhir, seseorang berkata dentang mengeluh tentang Factory :: Type menjadi pribadi ketika digunakan sebagai teman, jadi buat publik jika itu yang terjadi. Mengeksposnya tidak ada salahnya.
sumber
Saya memiliki masalah yang sama, tetapi tidak ada jawaban yang ada yang benar-benar memuaskan karena saya perlu menyampaikan argumen kepada konstruktor yang dilindungi. Selain itu, saya perlu melakukan ini untuk beberapa kelas, masing-masing mengambil argumen yang berbeda.
Untuk itu, dan membangun beberapa jawaban yang ada yang semuanya menggunakan metode serupa, saya sajikan sedikit nugget ini:
sumber
Akar masalahnya adalah bahwa jika fungsi atau kelas teman Anda membuat panggilan tingkat rendah ke konstruktor Anda, mereka harus berteman juga. std :: make_share bukan fungsi yang memanggil konstruktor Anda, jadi berteman itu tidak ada bedanya.
std :: _ Ref_count_obj sebenarnya memanggil konstruktor Anda, jadi ia harus menjadi teman. Karena itu agak tidak jelas, saya menggunakan makro
Kemudian deklarasi kelas Anda terlihat cukup sederhana. Anda bisa membuat makro tunggal untuk mendeklarasikan ptr dan kelas jika Anda mau.
Ini sebenarnya masalah penting. Untuk membuat kode portabel yang dapat dipelihara, Anda perlu menyembunyikan sebanyak mungkin implementasi.
menyembunyikan bagaimana Anda menangani pointer cerdas Anda sedikit, Anda harus yakin untuk menggunakan typedef Anda. Tetapi jika Anda selalu harus membuatnya menggunakan make_share, itu mengalahkan tujuannya.
Contoh di atas memaksa kode menggunakan kelas Anda untuk menggunakan konstruktor penunjuk pintar Anda, yang berarti bahwa jika Anda beralih ke penunjuk cerdas yang baru, Anda mengubah deklarasi kelas Anda dan Anda memiliki peluang yang layak untuk diselesaikan. JANGAN berasumsi bahwa bos atau proyek Anda berikutnya akan menggunakan stl, boost, dll. Rencanakan untuk mengubahnya suatu hari nanti.
Melakukan ini selama hampir 30 tahun, saya telah membayar harga yang besar dalam waktu, rasa sakit dan efek samping untuk memperbaiki ini ketika itu dilakukan salah tahun yang lalu.
sumber
std::_Ref_count_obj
adalah detail implementasi. Itu berarti sementara solusi ini mungkin bekerja untuk Anda, untuk saat ini, di platform Anda. Tetapi itu mungkin tidak bekerja untuk orang lain dan mungkin berhenti bekerja kapan saja kompiler Anda memperbarui atau mungkin bahkan jika Anda hanya mengubah flag kompilasi.Anda bisa menggunakan ini:
sumber
std::make_shared
.sumber