Neden "X ad alanı kullanılıyor?" sınıf / yapı düzeyi içinde izin verilmiyor mu?


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

Düzenleme : Arkasındaki motivasyonu bilmek ister.


1
@pst: C # gibi bir şey yok using namespace. C # benzer bir şeye izin verir, ancak yalnızca dosya kapsamında. C ++ 'lar using namespacebir ad alanını diğerine eklemenize izin verir.
Billy ONeal

2
Bu sorunun kopyası mı?
greatwolf

@ZachSaw, endişenizi anlıyorum. Alaka düzeyine göre Qn'yi kapatmaya çalıştınız. Bu gönderi, standarda daha objektif bir cevap ve referans içerdiğinden, onu açık tuttum. Geçmişte, eski Qn'imin çoğu yeni Qn tarafından kapatıldı .. bazen benim tarafımdan, bazen başkaları tarafından. Bu kararın uygun olmadığını düşünüyorsanız lütfen Elmas Modlarını işaretleyin. Alınmadım. :-)
iammilind

@iammilind daha az TBH'yi önemsemedi. SO bugünlerde bir karmaşa. Ancak cevap gerçekten "daha objektif cevap ve standarda referans" içerdiğinden "Tam olarak bilmiyorum" ile başlayan bir gönderiyi işaretlemek. Haha.
Zach Saw

@ZachSaw, sadece kabul edilen cevaptan değil, genel gönderiden bahsediyordum. Evet nesneldir ancak standart alıntı bu cevapta yer almaktadır . "Bilmiyorum" ile başlar, çünkü standart bile olsa, içeride "ad alanı kullanımına" neden izin verilmediğini haklı çıkarmaz class/struct. Sadece buna izin verilmiyor. Ancak kabul edilen cevap, buna izin vermemek için çok mantıklı bir mantığı tartışıyor. yani nerede dikkate alınmalı Hello::Worldve nerede dikkate alınmalı World. Umarım bu şüpheyi giderir.
iammilind

Yanıtlar:


36

Tam olarak bilmiyorum, ancak benim tahminim sınıf kapsamında buna izin verilmesi kafa karışıklığına neden olabilir:

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?
}

Bunu yapmanın bariz bir yolu olmadığından, standart sadece yapamayacağınızı söylüyor.

Şimdi, ad alanı kapsamlarından bahsederken bunun daha az kafa karıştırıcı olmasının nedeni:

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, bu nedeni düşündüm, ama aynı şey using namespace Hello;diğerinin içinde namespacede geçerli (ve externiçindeki işlevi ilan etmek).
iammilind

10
Kafa karıştırıcı olduğunu sanmıyorum. C ++ varsayımlarla ilgili değildir. İzin verilseydi, C ++ ISO komitesi dil belirtiminde belirtmiş olacaktı. O zaman kafa karıştırıcı olduğunu söylemezsin. Aksi takdirde, bunun bile kafa karıştırıcı olduğu söylenebilir : ideone.com/npOeD ... ancak bu tür kodlama için kural spesifikasyonda belirtilir.
Nawaz

1
@Nawaz: Dilin çoğu kullanıcısı. C ++ 'nın tahminlerle ilgili olduğunu hiç söylemedim. Spesifikasyon tasarlandığında, çoğu programcının önceden bekleyeceği davranışla tasarlandığını söylüyorum. Ve kağıt üzerindeki kurallar çoğu zaman kafa karıştırıcıdır - standart, net olmaya çalışır, ancak her zaman başarılı olmaz.
Billy ONeal

6
İlk örnekte, şöyle olmalıdır: Hello::World Blah::DoSomething()veya Blah::World Blah::DoSomething()(izin verildiyse), bir üye işlev tanımının dönüş türü, dildeki sınıf kapsamında kabul edilmez, bu nedenle nitelenmesi gerekir. usingBir typedef Hello::World World;sınıf kapsamıyla değiştirmenin geçerli bir örneğini düşünün . Bu yüzden orada sürpriz olmamalı.
David Rodríguez - dribeas

2
İzin verilseydi, sözcüksel kapsam düzeyinde uygulanacağına inanıyorum. Bence bu neredeyse hiç şaşırtıcı olmayan "bariz" bir çözüm.
Thomas Eding

19

Çünkü C ++ standardı bunu açıkça yasaklamaktadır. C ++ 03 §7.3.4'ten [namespace.udir]:

kullanma yönergesi :
    isim - alanı :: opt  iç içe-isim-belirleyici kullanma tercih  isim-alanı-ismi ;

Kullanım yönergesi sınıf kapsamında görünmez, ancak ad alanı kapsamında veya blok kapsamında görünebilir. [Not: using-yönergesinde bir isim-alanı-ismi aranırken, sadece isim-alanı isimleri dikkate alınır, bkz. 3.4.6. ]

C ++ standardı bunu neden yasaklıyor? Bilmiyorum, dil standardını onaylayan ISO komitesi üyelerinden birine sorun.


48
Yine teknik olarak doğru ama faydasız başka bir cevap; en kötü tür. 1) Komiteden daha fazla kişi cevabı biliyor. 2) komite üyeleri SO 3'e katılır, cevabı bilmiyorsanız (sorunun ruhu göz önüne alındığında) neden cevap veresiniz?
Catskul

7
@Catskul: Bu gereksiz bir cevap değil. Standardın bunu açıkça ele aldığını ve yasakladığını bilmek çok yararlıdır. En çok oylanan cevabın "tam olarak bilmiyorum" ile başlaması da ironik. Ayrıca, "standart yasaklıyor", "derleyici buna izin vermediği için izin verilmiyor" ile aynı değildir, çünkü ikinci durum aşağıdaki gibi takip sorularına cevap vermez: derleyicimle ilgili bir sorun mu var? derleyici standart uyumlu değil mi? farkında olmadığım başka şeylerin bir yan etkisi mi? vb.
antonone

9

Mantığın muhtemelen kafa karıştırıcı olacağına inanıyorum. Şu anda, bir sınıf düzeyi tanımlayıcı işlenirken, arama önce sınıf kapsamında ve ardından çevreleyen ad alanında arama yapacaktır. using namespaceSınıf düzeyinde izin vermenin, aramanın şimdi nasıl gerçekleştirildiği üzerinde oldukça bazı yan etkileri olacaktır. Özellikle, söz konusu sınıf kapsamının kontrol edilmesi ile çevreleyen ad alanının kontrol edilmesi arasında bazen yapılması gerekir. Yani: 1) sınıf düzeyini ve kullanılan ad alanı düzeyi aramalarını birleştirin, 2) sınıf kapsamından sonra , ancak herhangi bir sınıf kapsamından önce kullanılan ad alanını arayın, 3) kullanılan ad alanını çevreleyen ad alanından hemen önce arayın. 4) arama, çevreleyen ad alanıyla birleştirildi.

  1. Bu sınıf düzeyinde bir tanımlayıcı olacaktır büyük bir fark, yapacak gölge çevreleyen ad alanındaki herhangi bir tanımlayıcı, fakat olmaz gölge bir kullanılan ad. Farklı bir ad alanındaki bir sınıftan ve aynı ad alanından kullanılan ad alanına erişimin farklı olmasının etkisi garip olacaktır:

.

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. Bu sınıf kapsamının hemen arkasına bakın. Bu, temel sınıfların üyelerini gölgelemek gibi garip bir etkiye sahip olurdu. Geçerli arama, sınıf ve ad alanı düzeyi aramalarını karıştırmaz ve sınıf araması gerçekleştirirken , kapsayıcı ad alanını dikkate almadan önce temel sınıflara kadar gidecektir . Davranış, ad alanını çevreleyen ad alanına benzer bir düzeyde dikkate almaması açısından şaşırtıcı olacaktır. Yine, kullanılan ad alanına, çevreleyen ad alanına göre öncelik verilecektir.

.

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. Çevreleyen ad alanından hemen önce arama yapın. Bu yaklaşımla ilgili sorun, yine birçokları için şaşırtıcı olacağıdır. Aşağıdaki kodun aynı anda görülememesi için ad alanının farklı bir çeviri biriminde tanımlandığını düşünün:

.

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. Çevreleyen ad alanıyla birleştirin. Bu, usingbildirimi ad alanı düzeyinde uygulamakla tam olarak aynı etkiye sahip olacaktır . Buna herhangi bir yeni değer katmaz, ancak diğer yandan derleyici uygulayıcılarının aranmasını karmaşıklaştırır. Ad alanı tanımlayıcı araması, artık aramanın kodun neresinden tetiklendiğinden bağımsızdır. Bir sınıfın içindeyken, arama tanımlayıcıyı sınıf kapsamında bulamazsa, ad alanı aramasına geri döner, ancak bu, bir işlev tanımında kullanılan ad alanı aramasının aynısıdır, yeni durumu korumaya gerek yoktur. Ne zaman usingbeyan ad düzeyinde bulunursa, içeriği kullanılan ad olan getirdi için o ad içine tüm ad içeren aramalarının. Eğerusing namespace sınıf düzeyinde izin verildiğinde, aramanın nereden tetiklendiğine bağlı olarak tam olarak aynı ad alanının ad alanı araması için farklı sonuçlar olacaktır ve bu, aramanın uygulanmasını hiçbir ek değer olmaksızın çok daha karmaşık hale getirecektir.

Neyse, benim öneri değil istihdam using namespacehiç beyanname. Tüm ad alanlarının içeriğini akılda tutmak zorunda kalmadan kodu mantıklı hale getirir.


1
Kullanmanın örtük tuhaflıklar yaratma eğiliminde olduğuna katılıyorum. Ancak bazı kütüphaneler usingvar olan gerçeği etrafında tasarlanabilir . Derin iç içe geçmiş uzun ad alanlarındaki şeyleri kasıtlı olarak bildirerek. Örneğin glm, bunu yapar ve müşteri kullandığında özellikleri etkinleştirmek / sunmak için birden fazla numara kullanır using.
v.oddou

STL'de bile doğru 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

Açıklık ve kapalılık nedeniyle buna büyük olasılıkla izin verilmiyor .

  • C ++ 'daki sınıflar ve yapılar her zaman kapalı varlıklardır. Tam olarak tek bir yerde tanımlanırlar (beyanı ve uygulamayı bölebilirsiniz).
  • ad alanları isteğe bağlı olarak açılabilir, yeniden açılabilir ve genişletilebilir.

Ad alanlarını sınıflara aktarmak, bunun gibi komik durumlara yol açar:

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.
}

Veya sınıf üyelerini içe aktarılan isimlerle tanımlayamazdık. Bu yapının , her satır içi üye işlev gövdesine bu satırı koymak gibi namespace Foo, tür tanımındaki tüm kodlar için arama sırasına eklenmesine izin verin struct Bar, ancak ayraç veya eşit başlatıcılar vb. İçin de etkin olacaktır. Ama yine de using namespaceBir üye işlev gövdesinin içinde olduğu gibi, kapanış parantezinde sona erer . Şimdi maalesef, kapsayıcı ad alanını kirletmeden bir küme ayracı veya eşitleme başlatıcısında Koenig-with-fallback aramasını kullanmanın herhangi bir yolu yok gibi görünüyor.
Ben Voigt

0

Sanırım dilin bir kusuru. Aşağıdaki geçici çözümü kullanabilirsiniz. Bu geçici çözümü göz önünde bulundurarak, dilin değiştirileceği durum için ad uyuşmazlıkları çözümü önermek kolaydır.

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
{
}

Lütfen biraz açıklama ekleyebilir misiniz?
Kishan Bharda

Evet, bazı yorumlar ekledim
naprimeroleg
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.