Resmi olarak, typename ne için?


131

Bazen gccşablonları kullanırken ortaya çıkan gerçekten çözülemez bazı hata mesajları gördüm ... Özellikle, görünüşte doğru olan bildirimlerin, çok tuhaf derleme hatalarına neden olduğu ve bunun önekini ekleyerek sihirli bir şekilde ortadan kalktığı sorunlar yaşadım.typename anahtar kelimenin başına anahtar kelimeyi . deklarasyon ... (Örneğin, geçen hafta, iki yineleyiciyi başka bir şablon sınıfının üyeleri olarak ilan ediyordum ve bunu yapmak zorunda kaldım) ...

Hikaye nedir typename?


Yanıtlar:


207

Josuttis'in kitabından bir alıntı:

Anahtar kelime typename, takip eden tanımlayıcının bir tür olduğunu belirtmek için tanıtıldı. Aşağıdaki örneği düşünün:

template <class T>
Class MyClass
{
  typename T::SubType * ptr;
  ...
};

Burada bir tür typenameolduğunu açıklığa kavuşturmak için kullanılır . Böylece, tipe bir göstericidir . Olmazsa , statik bir üye olarak kabul edilir. BöyleceSubTypeclass TptrT::SubTypetypenameSubType

T::SubType * ptr

değerinin çarpımı olacaktır SubTypeÇeşidi Tile ptr.


2
Harika kitap. Bir kez okuyun ve isterseniz referans olarak saklayın.
deft_code

1
Zeki okuyucu, üye bildirimi için dilbilgisi tarafından çarpma ifadesine izin verilmediğini anlayacaktır. Böyle, C ++ 20 olarak gerek dağıtımının bunun için typename(gerçi hepsi değil!).
Davis Herring

Beni ikna etmedi. Şablon başlatıldıktan sonra, T ::
Subtype

36

Stan Lippman'ın BLog gönderisi şunları öneriyor: -

Stroustrup , var olan programları elbette bozabilecek yeni bir anahtar sözcük tanıtmak yerine bir tür parametresi belirtmek için mevcut sınıf anahtar sözcüğünü yeniden kullandı . Bu, yeni bir anahtar kelimenin dikkate alınmaması değildi - sadece potansiyel kesintisi göz önüne alındığında gerekli görülmemesi. Ve ISO-C ++ standardına kadar, bir tür parametresi bildirmenin tek yolu buydu.

Dolayısıyla Stroustrup, aşağıdaki nedenlerle standartta sonradan değiştirilen yeni bir anahtar kelime eklemeden class anahtar kelimesini yeniden kullandı.

Örnek verildiği gibi

template <class T>
class Demonstration {
public:
void method() {
    T::A *aObj; // oops …
     // …
};

dil grameri T::A *aObj;aritmetik bir ifade olarak yanlış yorumladığından , adı verilen yeni bir anahtar kelime tanıtıldıtypename

typename T::A* a6;

derleyiciye sonraki ifadeyi bir bildirim olarak ele alması talimatını verir.

Anahtar kelime maaş bordrosunda olduğundan, sınıf anahtar kelimesini yeniden kullanma konusundaki orijinal kararın neden olduğu kafa karışıklığını neden düzeltmeyelim ?

Bu yüzden ikisine de sahibiz

Sen bir göz olabilir bu yazı , kesinlikle size yardımcı olacaktır, ben sadece kadar I could olarak ondan çıkarılan


Evet, ama o zaman typenamemevcut anahtar kelimeyi classaynı amaç için kullanabilseydiniz neden yeni bir anahtar kelime gerekliydi ?
Jesper

5
@Jesper: Bence Xenus'un cevabı burada kafa karıştırıcı. typenameAyrıştırma sorununu Naveen'in yanıtında Josuttis'ten alıntı yaparak açıklandığı gibi düzeltmek için gerekli hale geldi. ( classBu yere a eklemenin işe yarayacağını sanmıyorum .) Ancak bu durum için yeni anahtar kelime kabul edildikten sonra, şablon argüman bildirimlerinde de buna izin verildi ( veya bu tanımlar mı? ), Çünkü classher zaman bir şekilde yanıltıcı.
sbi

13

Kodu düşünün

template<class T> somefunction( T * arg )
{
    T::sometype x; // broken
    .
    .

Ne yazık ki, derleyicinin psişik olması gerekmiyor ve T :: sometype'ın T'nin bir tür adına mı yoksa statik bir üyesine mi başvuracağını bilmiyor. Yani, typenamebunu söylemek için kullanılır:

template<class T> somefunction( T * arg )
{
    typename T::sometype x; // works!
    .
    .

6

Bağımlı olarak adlandırılan grubun bir üyesine başvurduğunuz bazı durumlarda tip olarak ("şablon parametresine bağlı" anlamına gelir), derleyici sonuçta ortaya çıkan yapının anlamsal anlamını her zaman açık bir şekilde çıkaramaz, çünkü bunun ne tür bir ad olduğunu bilmez. (yani bir türün adı, bir veri üyesinin adı veya başka bir şeyin adı olması). Bu gibi durumlarda, derleyiciye adın o bağımlı türün bir üyesi olarak tanımlanan bir tür adına ait olduğunu açıkça söyleyerek durumu netleştirmeniz gerekir.

Örneğin

template <class T> struct S {
  typename T::type i;
};

Bu örnekte typenamekodun derlenmesi için gerekli olan anahtar kelime .

Bağımlı türde bir şablon üyesine, yani bir şablonu belirten bir ada başvurmak istediğinizde de aynı şey olur. Ayrıca, templatefarklı yerleştirilmiş olmasına rağmen , anahtar kelimeyi kullanarak derleyiciye yardım etmelisiniz.

template <class T> struct S {
  T::template ptr<int> p;
};

Bazı durumlarda her ikisinin de kullanılması gerekebilir

template <class T> struct S {
  typename T::template ptr<int>::type i;
};

(sözdizimini doğru aldıysam).

Elbette, anahtar kelimenin başka bir rolü typenameşablon parametre bildirimlerinde kullanılmaktır.


Daha fazla (arka plan) bilgi için ayrıca bkz . C ++ typename anahtar kelimesinin açıklaması .
Atafar

5

İşin sırrı, bir şablonun bazı türler için özelleştirilebilmesinde yatmaktadır. Bu, aynı zamanda birkaç tip için tamamen farklı arayüzü tanımlayabileceği anlamına gelir. Örneğin şunları yazabilirsiniz:

template<typename T>
struct test {
    typedef T* ptr;
};

template<>         // complete specialization 
struct test<int> { // for the case T is int
    T* ptr;
};

Bunun neden yararlı olduğu sorulabilir ve gerçekten de: Bu gerçekten işe yaramaz görünüyor. Ama örneğin akılda almak tipi diğeri için tamamen farklı görünüyor s. Kuşkusuz, türü bir türden farklı bir şeye değiştirmez, ancak yine de olabilir.std::vector<bool>referenceTreference

Şimdi bu testşablonu kullanarak kendi şablonlarınızı yazarsanız ne olur ? Bunun gibi bir şey

template<typename T>
void print(T& x) {
    test<T>::ptr p = &x;
    std::cout << *p << std::endl;
}

nedeniyle senin için uygun gibi görünüyor bekliyoruz o test<T>::ptrtürüdür. Ancak derleyici bilmiyor ve aslında standart tarafından tersini beklemesi bile tavsiye ediliyor, test<T>::ptrbir tür değil. Derleyiciye ne beklediğinizi söylemek için typenameönce bir. Doğru şablon şuna benzer

template<typename T>
void print(T& x) {
    typename test<T>::ptr p = &x;
    std::cout << *p << std::endl;
}

Alt satır: typenameŞablonlarınızda iç içe bir şablon türü kullandığınızda önce eklemeniz gerekir. (Elbette yalnızca şablonunuzun bir şablon parametresi bu iç şablon için kullanılıyorsa.)


5

İki kullanım:

  1. Bağımsız templatedeğişken anahtar kelime olarak (yerine class)
  2. Bir typenameanahtar sözcük, derleyiciye bir tanımlayıcının bir tür olduğunu söyler (statik bir üye değişkeni yerine)
template <typename T> class X  // [1]
{
    typename T::Y _member;  // [2] 
}

4

Sanırım tüm cevaplar, typenameanahtar kelimenin iki farklı durumda kullanıldığından bahsetti :

a) Bir şablon türü parametresi bildirirken. Örneğin

template<class T> class MyClass{};        // these two cases are
template<typename T> class MyNewClass{};  // exactly the same.

Aralarında hiçbir fark olmayan ve TAM OLARAK aynıdır.

b) Bir şablon için iç içe geçmiş bağımlı bir tür adı kullanmadan önce .

template<class T>
void foo(const T & param)
{
   typename T::NestedType * value; // we should use typename here
}

Kullanılmaması typenameayrıştırma / derleme hatalarına yol açar.

İkinci duruma eklemek istediğim, Scot Meyers kitabının Etkili C ++ ' da belirtildiği gibi , iç içe geçmiş bir bağımlı tür adındantypename önce kullanmanın bir istisnası olmasıdır . İstisna kullanırsanız olmasıdır iç içe bağımlı tür adı bir şekilde ya temel sınıf veya içinde üye başlatma listesinde , kullanmak olmamalıdır orada:typename

template<class T>
class D : public B<T>::NestedType               // No need for typename here
{
public:
   D(std::string str) : B<T>::NestedType(str)   // No need for typename here
   {
      typename B<T>::AnotherNestedType * x;     // typename is needed here
   }
}

Not: Kullanımı typenameikinci durum için (yani iç içe bağımlı tip adından önce) 20 C ++ beri gerekli değildir.


2
#include <iostream>

class A {
public:
    typedef int my_t;
};

template <class T>
class B {
public:
    // T::my_t *ptr; // It will produce compilation error
    typename T::my_t *ptr; // It will output 5
};

int main() {
    B<A> b;
    int my_int = 5;
    b.ptr = &my_int;
    std::cout << *b.ptr;
    std::cin.ignore();
    return 0;
}
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.