C ++ 'da “using” anahtar kelimesinin arkasındaki mantık nedir?


145

C ++ 'da "using" anahtar kelimesinin arkasındaki mantık nedir?

Farklı durumlarda kullanılır ve ben tüm bunların ortak bir şey olup olmadığını bulmaya çalışıyorum ve "using" anahtar kelime gibi kullanılır bir nedeni vardır.

using namespace std; // to import namespace in the current namespace
using T = int; // type alias
using SuperClass::X; // using super class methods in derived class

53
Standart komite C ++ dilbilgisine yeni anahtar kelimeler eklemekten nefret eder.
İnternetler catz

4
@tehinternetsismadeofcatz Eğer gerçekten mantık buysa, lütfen affedersiniz gidip kendimi öldüreceğim.
user3111311 26:13

62
@ user3111311: Yeni ayrılmış sözcükleri tanıtmanın sonuçlarını biliyorsunuz, değil mi? Bu, tanımlayıcı adı olarak kullanılan tüm kodların aniden derlenemediği anlamına gelir. Bu KÖTÜ BİR ŞEY. Örneğin, C ++ olarak derlenemeyen birçok C kodu vardır, çünkü böyle şeyler içerir int class;. C ++ kodu aniden geçerli C ++ olarak durduysa daha da kötü olurdu.
Ben Voigt

7
@BenVoigt: int class;C ++ kullanarak C kodunun derlenmeyeceği tamamen kötü bir şey değil. C kodunun C olarak derlenmesini garanti etmek için kullanılabilir. C ve C ++ 'nın iki farklı dil olduğunu unutmak çok kolaydır - ve pratik olarak, geçerli C ve geçerli C ++, ancak farklı semantiklere sahip kod vardır.
Keith Thompson

1
Bu açıdan, usingbundan daha kötü (veya daha iyi) değildir static. IMHO, yeni anahtar kelimeler getirmeme noktası, internet tarafından catz ve Ben Voigt tarafından açıklandığı gibi çok önemlidir.
Cassio Neri

Yanıtlar:


114

C ++ 11'de, usingkullanıldığında anahtar sözcük type aliasaynıdır typedef.

7.1.3.2

Bir typedef-name, bir takma ad bildirimi ile de eklenebilir. Using anahtar sözcüğünü izleyen tanımlayıcı, typedef-name olur ve tanımlayıcıyı izleyen isteğe bağlı attribute-specifier-seq, bu typedef-name için görünür. Typedef belirleyicisi tarafından tanıtılmış gibi aynı anlambilime sahiptir. Özellikle, yeni bir tür tanımlamaz ve tür kimliğinde görünmez.

Bjarne Stroustrup pratik bir örnek sunmaktadır:

typedef void (*PFD)(double);    // C style typedef to make `PFD` a pointer to a function returning void and accepting double
using PF = void (*)(double);    // `using`-based equivalent of the typedef above
using P = [](double)->void; // using plus suffix return type, syntax error
using P = auto(double)->void // Fixed thanks to DyP

C ++ 11 öncesi, usinganahtar kelime üye işlevlerini kapsama alabilir. C ++ 11'de artık bunu yapıcılar için yapabilirsiniz (başka bir Bjarne Stroustrup örneği):

class Derived : public Base { 
public: 
    using Base::f;    // lift Base's f into Derived's scope -- works in C++98
    void f(char);     // provide a new f 
    void f(int);      // prefer this f to Base::f(int) 

    using Base::Base; // lift Base constructors Derived's scope -- C++11 only
    Derived(char);    // provide a new constructor 
    Derived(int);     // prefer this constructor to Base::Base(int) 
    // ...
}; 

Ben Voight, yeni bir anahtar kelime veya yeni sözdizimi sunmamanın mantığının arkasında oldukça iyi bir neden sunuyor. Standart, eski kodu olabildiğince kırmaktan kaçınmak istiyor. Bu nedenle, teklif belgelerinde Impact on the Standard, gibi bölümleri Design decisionsve bunların eski kodu nasıl etkileyebileceğini göreceksiniz . Bir teklifin gerçekten iyi bir fikir gibi göründüğü, ancak çekilmeyebileceği durumlar vardır, çünkü uygulanması çok zor, kafa karıştırıcı veya eski kodla çelişecektir.


İşte 2003 n1449 eski bir makale . Gerekçe şablonlarla ilgili görünmektedir. Uyarı: PDF'den kopyalama nedeniyle yazım hataları olabilir.

İlk olarak bir oyuncak örneği düşünelim:

template <typename T>
class MyAlloc {/*...*/};

template <typename T, class A>
class MyVector {/*...*/};

template <typename T>

struct Vec {
typedef MyVector<T, MyAlloc<T> > type;
};
Vec<int>::type p; // sample usage

Bu deyimle ilgili temel sorun ve bu teklif için temel motive edici gerçek, deyimin şablon parametrelerinin çıkartılamaz bağlamda görünmesine neden olmasıdır. Yani, şablon argümanlarını açıkça belirtmeden aşağıdaki foo işlevini çağırmak mümkün olmayacaktır.

template <typename T> void foo (Vec<T>::type&);

Yani sözdizimi biraz çirkin. Yuvadan kaçınmayı ::type tercih ederiz Aşağıdaki gibi bir şeyi tercih ederiz:

template <typename T>
using Vec = MyVector<T, MyAlloc<T> >; //defined in section 2 below
Vec<int> p; // sample usage

“Typedef template” teriminden özellikle kaçındığımızı ve karışıklığı önlemek için “using” ve “=” çiftini içeren yeni sözdizimini ürettiğimizi unutmayın: burada herhangi bir tür tanımlamıyoruz, bunun için bir eşanlamlı (diğer ad) sunuyoruz. şablon parametrelerini içeren bir tip kimliğinin (yani, tür ifadesi) soyutlanması. Şablon parametreleri tür ifadesinde çıkarılabilir bağlamlarda kullanılıyorsa, şablon takma adı bir şablon kimliği oluşturmak için her kullanıldığında, karşılık gelen şablon parametrelerinin değerleri çıkarılabilir - bunu daha fazlası takip edecektir. Her durumda, artık çıkartılabilir Vec<T>bağlamda çalışan genel işlevler yazmak mümkündür ve sözdizimi de geliştirilir. Örneğin foo'yu şu şekilde yeniden yazabiliriz:

template <typename T> void foo (Vec<T>&);

Burada, şablon takma adları önermenin temel nedenlerinden birinin, argüman çıkarımının ve çağrının foo(p) başarılı olacağının altını çiziyoruz.


Takip belgesi n1489 neden usingkullanmak yerine açıklamaktadır typedef:

Şablon takma adlarını tanıtmak için [4] yazısında olduğu gibi typedef anahtar kelimesinin (yeniden) kullanılması önerilmiştir:

template<class T> 
    typedef std::vector<T, MyAllocator<T> > Vec;

Bu gösterim, bir tür takma adı tanıttığı bilinen bir anahtar kelimeyi kullanma avantajına sahiptir. Bununla birlikte, takma adın bir tür değil, bir şablon belirttiği bir bağlamda bir tür adı için bir diğer ad tanıtmak için bilinen bir anahtar sözcüğü kullanmanın karışıklığı da vardır. Vecbir türün takma adı değildir ve typedef-name için alınmamalıdır. Ad Vec, ailenin bir adıdır std::vector< [bullet] , MyAllocator< [bullet] > > - burada madde işareti bir tür adı için yer tutucudur. Sonuç olarak “typedef” sözdizimini önermiyoruz. Öte yandan cümle

template<class T>
    using Vec = std::vector<T, MyAllocator<T> >;

şu şekilde okunabilir / yorumlanabilir: şu andan itibaren, Vec<T>eşanlamlı olarak kullanacağım std::vector<T, MyAllocator<T> >. Bu okuma ile, takma ad için yeni sözdizimi oldukça mantıklı görünüyor.

Bence burada önemli bir ayrım yapılmış, s türü yerine takma adlar var . Aynı belgeden başka bir alıntı:

Takma ad bildirimi bir tanım değildir, bir tanımdır. Bir takma ad, deklarasyonun sağ tarafının belirlediği tür için bir takma ad olarak deklaratif bir bölgeye bir ad verir. Bu teklifin özü, tür adı takma adlarıyla ilgilidir, ancak gösterim, ad alanı örtüşmesinin veya aşırı yüklenmiş işlevlerin adlandırılmasının alternatif yazımlarını sağlamak için açıkça genelleştirilebilir (daha fazla tartışma için ✁ 2.3'e bakın). [ Notum: Bu bölüm söz diziminin nasıl görünebileceğini ve teklifin bir parçası olmamasının nedenlerini tartışıyor. ] Dilbilgisi üretim takma adı bildiriminin bir typedef bildirimi veya bir ad-alanı takma adı tanımının kabul edilebilir olduğu her yerde kabul edilebilir.

Özet, rolü için using:

  • şablon takma adları (veya şablon typedefs, eski ad tercih edilir)
  • ad alanı takma adları (ör. namespace PO = boost::program_optionsve using PO = ...eşdeğeri)
  • belge diyor A typedef declaration can be viewed as a special case of non-template alias-declaration. Bu estetik bir değişikliktir ve bu durumda aynı kabul edilir.
  • bir şeyi kapsama getirmek (örneğin, namespace stdküresel kapsama), üye işlevleri, kurucuları devralma

O olamaz için kullanılabilir:

int i;
using r = i; // compile-error

Bunun yerine şunları yapın:

using r = decltype(i);

Bir dizi aşırı yükü adlandırmak.

// bring cos into scope
using std::cos;

// invalid syntax
using std::cos(double);

// not allowed, instead use Bjarne Stroustrup function pointer alias example
using test = std::cos(double);

2
@ user3111311 Aklınızda başka hangi anahtar kelimeler var? "Oto"? "Kayıt ol"?
Raymond Chen

2
using P = [](double)->void;, AFAIK, geçerli C ++ 11 değil. Ancak bu: using P = auto(double)->void;ve bir işlev türü üretir ( P*bir işlev işaretçisi gibi).
dyp

2
Adı Bjarne Stroustrup;) (Stroustrup'taki ikinci r'ye dikkat edin)
dyp

1
@RaymondChen: aslında registerbu kadar kötü gelmez, içinde:register X as Y
MFH

1
Ne yazık ki registerdeğişken bir bildirim başlar, bu yüzden zaten bir anlamı vardır. X tipi Y adlı bir kayıt değişkeni bildirin.
Raymond Chen
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.