Ruang nama sebaris adalah fitur versi perpustakaan yang mirip dengan versi simbol , tetapi diterapkan murni pada level C ++ 11 (mis. Lintas-platform) alih-alih menjadi fitur format biner yang dapat dieksekusi spesifik (mis. Platform-spesifik).
Ini adalah mekanisme di mana penulis perpustakaan dapat membuat tampilan namespace bersarang dan bertindak seolah-olah semua deklarasi berada di namespace sekitarnya (inline namespaces dapat bersarang, sehingga "lebih-bersarang" nama-nama merembes sampai ke non pertama -inama namespace dan lihat dan bertindak seolah-olah deklarasi mereka ada di salah satu ruang nama di antara keduanya).
Sebagai contoh, pertimbangkan penerapan STL untuk vector
. Jika kita memiliki ruang nama inline dari awal C ++, maka dalam C ++ 98 header <vector>
mungkin terlihat seperti ini:
namespace std {
#if __cplusplus < 1997L // pre-standard C++
inline
#endif
namespace pre_cxx_1997 {
template <class T> __vector_impl; // implementation class
template <class T> // e.g. w/o allocator argument
class vector : __vector_impl<T> { // private inheritance
// ...
};
}
#if __cplusplus >= 1997L // C++98/03 or later
// (ifdef'ed out b/c it probably uses new language
// features that a pre-C++98 compiler would choke on)
# if __cplusplus == 1997L // C++98/03
inline
# endif
namespace cxx_1997 {
// std::vector now has an allocator argument
template <class T, class Alloc=std::allocator<T> >
class vector : pre_cxx_1997::__vector_impl<T> { // the old impl is still good
// ...
};
// and vector<bool> is special:
template <class Alloc=std::allocator<bool> >
class vector<bool> {
// ...
};
};
#endif // C++98/03 or later
} // namespace std
Tergantung pada nilai __cplusplus
, salah satu atau vector
implementasi lainnya dipilih. Jika basis kode Anda ditulis dalam pra-C ++ 98 kali, dan Anda menemukan bahwa versi C ++ 98 vector
menyebabkan masalah bagi Anda ketika Anda memutakhirkan kompiler Anda, "yang harus Anda lakukan adalah menemukan referensi std::vector
di basis kode Anda dan ganti dengan std::pre_cxx_1997::vector
.
Datang standar berikutnya, dan vendor STL hanya mengulangi prosedur itu lagi, memperkenalkan namespace baru std::vector
dengan emplace_back
dukungan (yang membutuhkan C ++ 11) dan sebaris satu iff __cplusplus == 201103L
.
OK, jadi mengapa saya perlu fitur bahasa baru untuk ini? Saya sudah bisa melakukan yang berikut untuk memiliki efek yang sama, bukan?
namespace std {
namespace pre_cxx_1997 {
// ...
}
#if __cplusplus < 1997L // pre-standard C++
using namespace pre_cxx_1997;
#endif
#if __cplusplus >= 1997L // C++98/03 or later
// (ifdef'ed out b/c it probably uses new language
// features that a pre-C++98 compiler would choke on)
namespace cxx_1997 {
// ...
};
# if __cplusplus == 1997L // C++98/03
using namespace cxx_1997;
# endif
#endif // C++98/03 or later
} // namespace std
Bergantung pada nilai __cplusplus
, saya mendapatkan salah satu atau yang lain dari implementasi.
Dan Anda akan hampir benar.
Pertimbangkan kode pengguna C ++ 98 yang valid berikut ini (sudah diizinkan untuk sepenuhnya mengkhususkan templat yang tinggal di namespace std
dalam C ++ 98):
// I don't trust my STL vendor to do this optimisation, so force these
// specializations myself:
namespace std {
template <>
class vector<MyType> : my_special_vector<MyType> {
// ...
};
template <>
class vector<MyOtherType> : my_special_vector<MyOtherType> {
// ...
};
// ...etc...
} // namespace std
Ini adalah kode yang benar-benar valid di mana pengguna memasok implementasi sendiri dari vektor untuk satu set jenis di mana ia tampaknya tahu implementasi yang lebih efisien daripada yang ditemukan dalam (salinannya) STL.
Tetapi : Ketika mengkhususkan suatu templat, Anda perlu melakukannya di namespace yang dideklarasikan. Standar mengatakan itu vector
dideklarasikan dalam namespace std
, jadi di situlah pengguna berhak mengharapkan untuk mengkhususkan jenis.
Kode ini berfungsi dengan namespace non-versi std
, atau dengan fitur namespace C ++ 11 inline, tetapi tidak dengan trik versi yang digunakan using namespace <nested>
, karena itu memperlihatkan detail implementasi yang namespace sebenarnya yang vector
didefinisikan tidak std
secara langsung.
Ada lubang lain di mana Anda dapat mendeteksi namespace bersarang (lihat komentar di bawah), tetapi inpasi namespace pasang semuanya. Dan hanya itu yang ada untuk itu. Sangat berguna untuk masa depan, tetapi AFAIK Standar tidak meresepkan nama namespace inline untuk pustaka standarnya sendiri (meskipun, saya senang terbukti salah tentang hal ini), jadi itu hanya dapat digunakan untuk perpustakaan pihak ketiga, tidak standar itu sendiri (kecuali vendor kompiler menyetujui skema penamaan).
using namespace V99;
tidak berfungsi dalam contoh Stroustrup.std::cxx_11
. Tidak setiap kompiler akan selalu mengimplementasikan semua versi lama dari pustaka standar, meskipun saat ini tergoda untuk berpikir bahwa akan sangat sedikit beban untuk meminta implementasi yang ada untuk pergi di yang lama ketika mereka menambahkan yang baru, karena sebenarnya mereka semua bagaimanapun juga. Saya kira apa yang bisa dilakukan oleh standar adalah menjadikannya opsional, tetapi dengan nama standar jika ada.using namespace A
dalam namespace B membuat nama dalam namespace B menyembunyikan nama dalam namespace A jika Anda mencariB::name
- tidak demikian dengan inp namespace inline).ifdef
s untuk implementasi vektor penuh? Semua implementasi akan berada dalam satu namespace tetapi hanya satu dari mereka yang akan didefinisikan setelah preprocessingusing
kata kunci) yang sepenuhnya memenuhi syarat .http://www.stroustrup.com/C++11FAQ.html#inline-namespace (dokumen yang ditulis oleh dan dikelola oleh Bjarne Stroustrup, yang Anda pikir harus mengetahui sebagian besar motivasi untuk sebagian besar fitur C ++ 11. )
Menurut itu, ini memungkinkan versi untuk kompatibilitas ke belakang. Anda mendefinisikan beberapa ruang nama bagian dalam, dan membuat yang terbaru
inline
. Atau, yang default untuk orang-orang yang tidak peduli dengan versi. Saya kira yang paling baru bisa menjadi versi masa depan atau mutakhir yang belum standar.Contoh yang diberikan adalah:
Saya tidak langsung mengerti mengapa Anda tidak memasukkan
using namespace V99;
namespaceMine
, tetapi saya tidak harus sepenuhnya memahami kasus penggunaan untuk mengambil kata-kata Bjarne untuk itu atas motivasi komite.sumber
f(1)
versi terakhir akan dipanggil dariV99
namespace inline ?using namespace Mine;
, danMine
namespace berisi semuanya dari inline namespaceMine::V99
.inline
dari fileV99.h
dalam rilis yang menyertakanV100.h
. Anda juga memodifikasiMine.h
pada saat yang sama, tentu saja, untuk menambahkan tambahan.Mine.h
adalah bagian dari perpustakaan, bukan bagian dari kode klien.V100.h
, mereka menginstal perpustakaan yang disebut "Milikku". Ada 3 file header dalam versi 99 dari "Milikku" -Mine.h
,V98.h
danV99.h
. Ada 4 file header dalam versi 100 dari "Mine" -Mine.h
,V98.h
,V99.h
danV100.h
. Pengaturan file header adalah detail implementasi yang tidak relevan bagi pengguna. Jika mereka menemukan beberapa masalah kompatibilitas yang berarti mereka perlu menggunakan secara khususMine::V98::f
dari beberapa atau semua kode mereka, mereka dapat menggabungkan panggilanMine::V98::f
dari kode lama dengan panggilan keMine::f
dalam kode yang baru ditulis.Mine
, daripada harus berspesialisasi dalamMine::V99
atauMine::V98
.Selain semua jawaban lainnya.
Namespace inline dapat digunakan untuk menyandikan informasi ABI atau Versi fungsi dalam simbol. Karena alasan ini mereka digunakan untuk memberikan kompatibilitas ABI mundur. Ruang nama sebaris memungkinkan Anda menyuntikkan informasi ke dalam nama hancur (ABI) tanpa mengubah API karena hanya memengaruhi nama simbol tautan.
Pertimbangkan contoh ini:
Misalkan Anda menulis fungsi
Foo
yang mengambil referensi ke objek mengatakanbar
dan tidak mengembalikan apa pun.Katakan di main.cpp
Jika Anda memeriksa nama simbol untuk file ini setelah mengompilasinya menjadi objek.
Sekarang, bisa jadi itu
bar
didefinisikan sebagai:Bergantung pada tipe Bangun,
bar
dapat merujuk ke dua jenis / tata letak yang berbeda dengan simbol tautan yang sama.Untuk mencegah perilaku seperti itu, kami membungkus struct kami
bar
menjadi namespace inline, di mana tergantung pada tipe Build simbol linkerbar
akan berbeda.Jadi, kita bisa menulis:
Sekarang jika Anda melihat file objek dari setiap objek yang Anda buat satu rilis menggunakan dan lainnya dengan flag debug. Anda akan menemukan bahwa simbol tautan juga menyertakan nama namespace sebaris. Pada kasus ini
Perhatikan keberadaan
rel
dandbg
dalam nama simbol.Sekarang, jika Anda mencoba menautkan debug dengan mode rilis atau sebaliknya, Anda akan mendapatkan kesalahan tautan sebagai kebalikan dari kesalahan runtime.
sumber
Saya benar-benar menemukan kegunaan lain untuk ruang nama sebaris.
Dengan Qt , Anda mendapatkan beberapa tambahan, fitur bagus menggunakan
Q_ENUM_NS
, yang pada gilirannya mengharuskan namespace yang melampirkan memiliki objek-meta, yang dideklarasikan denganQ_NAMESPACE
. Namun, agarQ_ENUM_NS
dapat berfungsi, harus ada yang sesuaiQ_NAMESPACE
dalam file yang sama ⁽¹⁾. Dan hanya ada satu, atau Anda mendapatkan kesalahan definisi duplikat. Ini, secara efektif, berarti bahwa semua enumerasi Anda harus berada di header yang sama. Yuck.Atau ... Anda dapat menggunakan ruang nama sebaris. Menyembunyikan enumerasi dalam
inline namespace
menyebabkan meta-objek memiliki nama yang berbeda, sementara mencari pengguna seperti namespace tambahan tidak ada ⁽²⁾.Jadi, mereka berguna untuk memecah hal-hal menjadi beberapa sub-namespace yang semuanya terlihat seperti satu namespace, jika Anda perlu melakukan itu untuk beberapa alasan. Tentu saja, ini mirip dengan menulis
using namespace inner
di namespace luar, tetapi tanpa pelanggaran KERING menulis nama namespace bagian dalam dua kali.Sebenarnya lebih buruk dari itu; itu harus di set kawat gigi yang sama.
Kecuali Anda mencoba mengakses objek-meta tanpa sepenuhnya memenuhi syarat, tetapi objek-meta hampir tidak pernah digunakan secara langsung.
sumber
Jadi untuk meringkas poin utama,
using namespace v99
daninline namespace
tidak sama, yang pertama adalah solusi untuk pustaka versi sebelum kata kunci khusus (inline) diperkenalkan di C ++ 11 yang memperbaiki masalah penggunaanusing
, sambil menyediakan fungsionalitas versi yang sama. Menggunakan yangusing namespace
digunakan untuk menyebabkan masalah dengan ADL (meskipun ADL sekarang tampaknya mengikutiusing
arahan), dan spesialisasi out-of-line kelas perpustakaan / fungsi dll oleh pengguna tidak akan bekerja jika dilakukan di luar namespace yang benar (yang namanya pengguna tidak akan dan tidak seharusnya tahu, yaitu pengguna harus menggunakan B :: abi_v2 :: daripada hanya B :: untuk spesialisasi untuk menyelesaikan).Ini akan menampilkan peringatan analisis statis
first declaration of class template specialization of 'myclass' outside namespace 'A' is a C++11 extension [-Wc++11-extensions]
. Tetapi jika Anda membuat namespace A inline, maka kompiler dengan benar menyelesaikan spesialisasi. Meskipun, dengan ekstensi C ++ 11, masalah ini hilang.Definisi out-of-line tidak menyelesaikan ketika menggunakan
using
; mereka harus dideklarasikan dalam blok namespace ekstensi bersarang / tidak bersarang (yang berarti pengguna perlu mengetahui versi ABI lagi, jika karena alasan apa pun mereka diizinkan untuk menyediakan implementasi fungsi mereka sendiri).Masalahnya hilang saat membuat B sebaris.
inline
Namespaces fungsi lain yang dimiliki adalah memungkinkan penulis perpustakaan untuk menyediakan pembaruan transparan ke perpustakaan 1) tanpa memaksa pengguna untuk refactor kode dengan nama namespace baru dan 2) mencegah kurangnya verbositas dan 3) memberikan abstraksi dari rincian yang tidak relevan dengan API, sementara 4) memberikan diagnosa dan perilaku linker bermanfaat yang sama yang menggunakan namespace non-inline. Katakanlah Anda menggunakan perpustakaan:Ini memungkinkan pengguna untuk menelepon
library::foo
tanpa perlu tahu atau memasukkan versi ABI dalam dokumentasi, yang terlihat lebih bersih. Menggunakanlibrary::abiverison129389123::foo
akan terlihat kotor.Ketika pembaruan dibuat
foo
, yaitu menambahkan anggota baru ke kelas, itu tidak akan mempengaruhi program yang ada di tingkat API karena mereka tidak akan menggunakan anggota DAN perubahan dalam nama namespace sebaris tidak akan mengubah apa pun di tingkat API karenalibrary::foo
akan tetap bekerja.Namun, untuk program yang menautkannya, karena nama namespace inline diubah menjadi nama simbol seperti namespace biasa, perubahan tidak akan transparan kepada tautan. Oleh karena itu, jika aplikasi tidak dikompilasi tetapi dihubungkan dengan versi baru perpustakaan, itu akan menyajikan simbol
abi_v1
tidak ditemukan kesalahan, daripada benar-benar menghubungkan dan kemudian menyebabkan kesalahan logika misterius saat runtime karena ketidakcocokan ABI. Menambahkan anggota baru akan menyebabkan kompatibilitas ABI karena perubahan dalam definisi tipe, bahkan jika itu tidak mempengaruhi program pada waktu kompilasi (level API).Dalam skenario ini:
Seperti menggunakan 2 ruang nama non-inline, ini memungkinkan untuk versi baru perpustakaan untuk ditautkan tanpa perlu mengkompilasi ulang aplikasi, karena
abi_v1
akan hancur dalam salah satu simbol global dan akan menggunakan definisi tipe yang lama (lama). Namun mengkompilasi ulang aplikasi akan menyebabkan referensi untuk diselesaikanlibrary::abi_v2
.Menggunakan
using namespace
kurang fungsional daripada menggunakaninline
(dalam definisi out of line tidak menyelesaikan) tetapi memberikan 4 keuntungan yang sama seperti di atas. Tetapi pertanyaan sebenarnya adalah, mengapa terus menggunakan solusi ketika sekarang ada kata kunci khusus untuk melakukannya. Ini praktik yang lebih baik, lebih sedikit verbose (harus mengubah 1 baris kode alih-alih 2) dan memperjelas maksudnya.sumber