Mengapa "menggunakan namespace X;" tidak diperbolehkan di dalam kelas / tingkat struct?


90
class C {
  using namespace std;  // error
};
namespace N {
  using namespace std; // ok
}
int main () {
  using namespace std; // ok
}

Edit : Ingin tahu motivasi di baliknya.


1
@pst: C # tidak memiliki yang seperti itu using namespace. C # memungkinkan sesuatu yang serupa, tetapi hanya pada lingkup file. C ++ using namespacememungkinkan Anda untuk menggabungkan satu namespace ke yang lain.
Billy ONeal

2
Duplikat dari pertanyaan ini ?
greatwolf

@ZachSaw, saya memahami kekhawatiran Anda. Telah mencoba menutup Qn berdasarkan relevansinya. Karena posting ini berisi jawaban yang lebih obyektif & referensi ke standar, saya tetap membukanya. Di masa lalu, banyak Qn lama saya ditutup oleh Qn yang lebih baru .. terkadang oleh saya terkadang oleh orang lain. Silakan tandai ke Diamond Mods, jika Anda merasa bahwa keputusan ini tidak tepat. Tidak ada perasaan keras. :-)
iammilind

@iammilind tidak peduli TBH. JADI kacau hari ini. Namun menandai postingan yang diawali dengan "Saya tidak tahu persis" karena jawaban sebenarnya mengandung "jawaban yang lebih objektif & mengacu pada standar". Ha ha.
Zach Melihat

@ZachSaw, saya tidak hanya berbicara tentang jawaban yang diterima, tetapi secara keseluruhan posting. Ya itu objektif tetapi kutipan standar terkandung dalam jawaban ini . Ini dimulai dengan "Saya tidak tahu", karena bahkan dalam standar, tidak dibenarkan mengapa "menggunakan namespace" tidak diperbolehkan di dalam class/struct. Itu tidak diperbolehkan. Tetapi jawaban yang diterima memang membahas alasan yang sangat logis untuk melarangnya. yaitu di mana harus mempertimbangkan Hello::Worlddan di mana harus mempertimbangkan World. Harapan yang menghilangkan keraguan.
iammilind

Jawaban:


36

Saya tidak tahu persis, tetapi dugaan saya adalah mengizinkan ini di ruang lingkup kelas dapat menyebabkan kebingungan:

namespace Hello
{
    typedef int World;
}

class Blah
{
    using namespace Hello;
public:
    World DoSomething();
}

//Should this be just World or Hello::World ?
World Blah::DoSomething()
{
    //Is the using namespace valid in here?
}

Karena tidak ada cara yang jelas untuk melakukan ini, standar hanya mengatakan Anda tidak bisa.

Sekarang, alasan mengapa ini tidak terlalu membingungkan ketika kita berbicara tentang cakupan namespace:

namespace Hello
{
    typedef int World;
}

namespace Other
{
    using namespace Hello;
    World DoSomething();
}

//We are outside of any namespace, so we have to fully qualify everything. Therefore either of these are correct:

//Hello was imported into Other, so everything that was in Hello is also in Other. Therefore this is okay:
Other::World Other::DoSomething()
{
    //We're outside of a namespace; obviously the using namespace doesn't apply here.
    //EDIT: Apparently I was wrong about that... see comments. 
}

//The original type was Hello::World, so this is okay too.
Hello::World Other::DoSomething()
{
    //Ditto
}

namespace Other
{
    //namespace Hello has been imported into Other, and we are inside Other, so therefore we never need to qualify anything from Hello.
    //Therefore this is unambiguiously right
    World DoSomething()
    {
        //We're inside the namespace, obviously the using namespace does apply here.
    }
}

5
1, Saya memikirkan alasan ini, tetapi kemudian hal yang sama berlaku untuk using namespace Hello;di dalam yang lain namespacejuga (dan menyatakan externfungsi di dalamnya).
iammilind

10
Saya tidak berpikir itu membingungkan. C ++ bukan tentang menebak-nebak. Jika diizinkan, maka komite C ++ ISO akan menentukan spesifikasi bahasa. Maka Anda tidak akan mengatakan itu membingungkan. Jika tidak, orang mungkin akan mengatakan bahkan ini membingungkan: ideone.com/npOeD ... tetapi kemudian aturan untuk pengkodean tersebut ditentukan dalam spesifikasi.
Nawaz

1
@Nawaz: Sebagian besar pengguna bahasa. Saya tidak pernah mengatakan C ++ adalah tentang tebakan. Saya mengatakan bahwa ketika spesifikasi dirancang, itu dirancang dengan perilaku apa yang diharapkan sebagian besar pemrogram sebelumnya. Dan aturan di atas kertas sering adalah membingungkan - upaya standar untuk menjadi ambigu tetapi tidak selalu berhasil.
Billy ONeal

6
Pada contoh pertama, seharusnya: Hello::World Blah::DoSomething()atau Blah::World Blah::DoSomething()(jika diizinkan), tipe kembalian dari definisi fungsi anggota tidak dianggap berada dalam cakupan kelas dalam bahasa tersebut, jadi harus memenuhi syarat. Pertimbangkan contoh yang valid untuk mengganti usingdengan a typedef Hello::World World;at class scope. Jadi seharusnya tidak ada kejutan di sana.
David Rodríguez - dribeas

2
Jika diizinkan, saya yakin ini akan diterapkan pada tingkat cakupan leksikal. Saya pikir ini adalah solusi yang "jelas" dengan hampir tidak ada kejutan.
Thomas Eding

19

Karena standar C ++ secara eksplisit melarangnya. Dari C ++ 03 §7.3.4 [namespace.udir]:

menggunakan-direktif :
    menggunakan namespace :: opt  nested-name-specifier opt  namespace-name ;

Sebuah penggunaan-direktif tidak akan muncul dalam ruang lingkup kelas, tetapi mungkin muncul dalam ruang lingkup namespace atau dalam lingkup blok. [Catatan: saat mencari nama-namespace di direktif-menggunakan, hanya nama-nama namespace yang dipertimbangkan, lihat 3.4.6. ]

Mengapa standar C ++ melarangnya? Saya tidak tahu, tanyakan kepada anggota komite ISO yang menyetujui standar bahasa.


48
Namun jawaban lain yang secara teknis benar tetapi tidak berguna; jenis yang paling buruk. 1) lebih banyak orang daripada sekedar panitia yang tahu jawabannya. 2) Anggota panitia mengikuti SO 3) jika tidak tahu jawabannya (mengingat semangat pertanyaan) mengapa menjawab sama sekali?
Catskul

7
@ Catskul: ini bukan jawaban yang tidak berguna. Sangat berguna untuk mengetahui bahwa standar secara eksplisit membahas ini dan melarangnya. Sungguh ironis bahwa jawaban yang paling disukai dimulai dengan "Saya tidak tahu persis". Juga, "standar melarangnya" tidak sama dengan "itu tidak diperbolehkan karena kompilator tidak mengizinkannya", karena kasus terakhir tidak akan menjawab pertanyaan lanjutan seperti: apakah ini masalah dengan kompilator saya? apakah kompilernya tidak memenuhi standar? apakah ini efek samping dari beberapa hal lain yang tidak saya sadari? dll.
antonone

9

Saya percaya bahwa alasannya adalah mungkin akan membingungkan. Saat ini, saat memproses pengenal tingkat kelas, pencarian pertama akan mencari di ruang lingkup kelas dan kemudian di ruang nama yang melingkupi. Mengizinkan using namespacedi tingkat kelas akan memiliki beberapa efek samping pada bagaimana pencarian sekarang dilakukan. Secara khusus, itu harus dilakukan kadang-kadang antara memeriksa ruang lingkup kelas tertentu dan memeriksa ruang nama yang melingkupinya. Yaitu: 1) menggabungkan tingkat kelas dan pencarian tingkat ruang nama yang digunakan, 2) mencari ruang nama yang digunakan setelah cakupan kelas tetapi sebelum ruang lingkup kelas lainnya, 3) mencari ruang nama yang digunakan tepat sebelum ruang nama yang melingkupi. 4) pencarian digabungkan dengan namespace yang melingkupi.

  1. Ini akan membuat perbedaan besar, di mana pengenal di tingkat kelas akan membayangi pengenal apa pun di ruang nama yang melingkupinya, tetapi tidak akan membayangi ruang nama yang digunakan . Efeknya akan aneh, karena akses ke namespace yang digunakan dari kelas di namespace yang berbeda dan dari namespace yang sama akan berbeda:

.

namespace A {
   void foo() {}
   struct B {
      struct foo {};
      void f() {
         foo();      // value initialize a A::B::foo object (current behavior)
      }
   };
}
struct C {
   using namespace A;
   struct foo {};
   void f() {
      foo();         // call A::foo
   }
};
  1. Cari tepat setelah cakupan kelas ini. Ini akan memiliki efek aneh membayangi anggota kelas dasar. Pencarian saat ini tidak mencampur pencarian tingkat kelas dan namespace, dan saat melakukan pencarian kelas, pencarian akan menuju ke kelas dasar sebelum mempertimbangkan namespace yang melingkupinya. Perilaku ini akan mengejutkan karena tidak mempertimbangkan namespace di tingkat yang mirip dengan namespace yang melingkupinya. Sekali lagi, namespace yang digunakan akan diprioritaskan di atas namespace yang melingkupinya.

.

namespace A {
   void foo() {}
}
void bar() {}
struct base {
   void foo();
   void bar();
};
struct test : base {
   using namespace A;
   void f() {
      foo();           // A::foo()
      bar();           // base::bar()
   }
};
  1. Cari tepat sebelum namespace yang melingkupi. Masalah dengan pendekatan ini sekali lagi akan mengejutkan banyak orang. Pertimbangkan bahwa namespace ditentukan dalam unit terjemahan yang berbeda, sehingga kode berikut tidak dapat dilihat sekaligus:

.

namespace A {
   void foo( int ) { std::cout << "int"; }
}
void foo( double ) { std::cout << "double"; }
struct test {
   using namespace A;
   void f() {
      foo( 5.0 );          // would print "int" if A is checked *before* the
                           // enclosing namespace
   }
};
  1. Gabungkan dengan namespace yang melingkupi. Ini akan memiliki efek yang sama persis dengan menerapkan usingdeklarasi di tingkat namespace. Ini tidak akan menambahkan nilai baru apa pun untuk itu, tetapi di sisi lain akan mempersulit pencarian untuk pelaksana compiler. Pencarian pengenal ruang nama sekarang tidak bergantung pada tempat pencarian kode dipicu. Saat berada di dalam kelas, jika pencarian tidak menemukan pengenal pada ruang lingkup kelas, ia akan kembali ke pencarian namespace, tetapi itu adalah pencarian namespace yang persis sama yang digunakan dalam definisi fungsi, tidak perlu mempertahankan status baru. Ketika usingdeklarasi ditemukan di tingkat namespace, konten namespace yang digunakan dibawa ke namespace itu untuk semua pencarian yang melibatkan namespace. Jikausing namespace diizinkan di tingkat kelas, akan ada hasil yang berbeda untuk pencarian namespace dari namespace yang sama persis tergantung dari mana pencarian dipicu, dan itu akan membuat implementasi pencarian jauh lebih kompleks tanpa nilai tambahan.

Bagaimanapun, rekomendasi saya adalah tidak menggunakan using namespacedeklarasi sama sekali. Itu membuat kode lebih sederhana untuk alasan tanpa harus mengingat semua konten ruang nama.


1
Saya setuju bahwa penggunaan cenderung menciptakan keanehan yang implisit. Tetapi beberapa perpustakaan mungkin dirancang berdasarkan fakta yang usingada. Dengan sengaja mendeklarasikan hal-hal dalam namespace panjang bersarang yang dalam. Misalnya glmmelakukan itu, dan menggunakan beberapa trik untuk mengaktifkan / menampilkan fitur saat klien menggunakannya using.
v.oddou

bahkan tepat di STL using namespace std::placeholders. cf en.cppreference.com/w/cpp/utility/functional/bind
v.oddou

@ v.oddou:namespace ph = std::placeholders;
David Rodríguez - dribeas

1

Ini mungkin tidak diizinkan karena keterbukaan vs ketertutupan.

  • Kelas dan struct di C ++ selalu merupakan entitas tertutup. Mereka didefinisikan tepat di satu tempat (meskipun Anda dapat memisahkan deklarasi dan implementasi).
  • namespace dapat dibuka, dibuka kembali, dan diperpanjang sesering mungkin.

Mengimpor namespace ke dalam kelas akan menyebabkan kasus lucu seperti ini:

namespace Foo {}

struct Bar { using namespace Foo; };

namespace Foo {
using Baz = int; // I've just extended `Bar` with a type alias!
void baz(); // I've just extended `Bar` with what looks like a static function!
// etc.
}

Atau kita TIDAK bisa mendefinisikan anggota kelas dengan nama yang diimpor. Biarkan konstruksi ini menambah namespace Foourutan pencarian untuk semua kode di dalam definisi tipe struct Bar, seperti meletakkan baris itu di setiap badan fungsi anggota sebaris, kecuali bahwa itu juga akan aktif untuk penginisialisasi brace-or-equal, dll. Tapi itu akan tetap kedaluwarsa pada penjepit penutup, sama seperti using namespacedi dalam badan fungsi anggota. Sayangnya, sekarang tampaknya tidak ada cara untuk menggunakan pencarian Koenig-dengan-fallback dalam penginisialisasi brace-or-equal tanpa mencemari namespace yang melingkupinya.
Ben Voigt

0

Saya pikir itu adalah cacat bahasa. Anda dapat menggunakan solusi di bawah ini. Mengingat solusi ini, mudah untuk menyarankan aturan resolusi konflik nama untuk kasus ketika bahasa akan diubah.

namespace Hello
{
    typedef int World;
}
// surround the class (where we want to use namespace Hello)
// by auxiliary namespace (but don't use anonymous namespaces in h-files)
namespace Blah_namesp {
using namespace Hello;

class Blah
{
public:
    World DoSomething1();
    World DoSomething2();
    World DoSomething3();
};

World Blah::DoSomething1()
{
}

} // namespace Blah_namesp

// "extract" class from auxiliary namespace
using Blah_namesp::Blah;

Hello::World Blah::DoSomething2()
{
}
auto Blah::DoSomething3() -> World
{
}

Bisakah Anda menambahkan penjelasan?
Kishan Bharda

Ya, saya telah menambahkan beberapa komentar
naprimeroleg
Dengan menggunakan situs kami, Anda mengakui telah membaca dan memahami Kebijakan Cookie dan Kebijakan Privasi kami.
Licensed under cc by-sa 3.0 with attribution required.