Gdzie i dlaczego muszę umieścić słowa kluczowe" template "i" typename"?

W szablonach, gdzie i dlaczego muszę umieszczać typename i template na nazwach zależnych? Czym właściwie są nazwy zależne? Mam następujący kod:

template <typename T, typename Tail> // Tail will be a UnionNode too.
struct UnionNode : public Tail {
    // ...
    template<typename U> struct inUnion {
        // Q: where to add typename/template here?
        typedef Tail::inUnion<U> dummy; 
    };
    template< > struct inUnion<T> {
    };
};
template <typename T> // For the last node Tn.
struct UnionNode<T, void> {
    // ...
    template<typename U> struct inUnion {
        char fail[ -2 + (sizeof(U)%2) ]; // Cannot be instantiated for any U
    };
    template< > struct inUnion<T> {
    };
};

Problem, który mam jest w typedef Tail::inUnion<U> dummy linii. Jestem całkiem pewien, że inUnion jest nazwą zależną, a VC++ ma rację w tym zadławieniu. Wiem też, że powinienem być w stanie dodać template gdzieś, aby powiedzieć kompilatorowi, że inUnion jest template-id. Ale gdzie dokładnie? I należy wtedy przyjąć, że inUnion jest szablonem klasowym, tzn. inUnion<U> nazywa typ, a nie funkcję?

Author: MSalters, 2009-03-04

6 answers

Aby przeanalizować program C++, kompilator musi wiedzieć, czy pewne nazwy są typami, czy nie. Poniższy przykład pokazuje, że:

t * f;

Jak należy to przeanalizować? W wielu językach kompilator nie musi znać znaczenia nazwy, aby analizować i zasadniczo wiedzieć, jakie działania wykonuje linia kodu. W C++ powyższe może jednak dawać znacznie różne interpretacje w zależności od tego, co oznacza t. Jeśli jest typem, to będzie deklaracją wskaźnika f. Jeśli jednak nie jest to typ, będzie to mnożenie. Tak więc Standard C++ mówi w paragrafie (3/7):

Niektóre nazwy oznaczają typy lub szablony. Ogólnie rzecz biorąc, za każdym razem, gdy napotkana jest nazwa, konieczne jest określenie, czy nazwa ta oznacza jeden z tych podmiotów przed kontynuowaniem analizy programu, który ją zawiera. Proces, który to określa, nazywa się name lookup.

Jak kompilator dowie się, do czego odnosi się nazwa t::x, Jeśli t odnosi się do parametru typu szablonu? x Może być statycznym elementem danych int, który można pomnożyć lub równie dobrze może być zagnieżdżoną klasą lub typedef, która może ulec deklaracji. Jeśli nazwa ma tę właściwość - że nie można jej sprawdzić, dopóki rzeczywiste argumenty szablonu nie są znane - to nazywa się ją zależną nazwą ("zależy" od parametrów szablonu).

Możesz polecić po prostu poczekać, aż użytkownik utworzy instancję szablonu:

poczekajmy, aż użytkownik utworzy instancję szablonu, a później dowiemy się prawdziwego znaczenia t::x * f;.

To zadziała i w rzeczywistości jest dozwolone przez Standard jako możliwe podejście wdrożeniowe. Kompilatory te zasadniczo kopiują tekst szablonu do wewnętrznego bufora i tylko wtedy, gdy potrzebna jest instancja, analizują szablon i ewentualnie wykrywają błędy w definicji. Ale zamiast przeszkadzać użytkownikom szablonu (biedni koledzy!) z błędy popełniane przez autora szablonu, inne implementacje decydują się na sprawdzenie szablonów na początku i podanie błędów w definicji tak szybko, jak to możliwe, zanim nastąpi instancja.

Więc musi być sposób, aby powiedzieć kompilatorowi, że niektóre nazwy są typami, a niektóre nie. {62]}

Słowo kluczowe "typename"

Odpowiedź brzmi: my decydujemy jak kompilator powinien to przetworzyć. Jeśli t::x jest nazwą zależną, to musimy ją poprzedzić przez typename aby powiedzieć kompilatorowi, aby parsował to w określony sposób. Standard mówi przy (14.6 / 2):

Nazwa używana w deklaracji lub definicji szablonu i zależna od parametru szablonu to zakłada się, że nie nadaje nazwy typowi, chyba że odpowiednie wyszukiwanie nazwy znajdzie nazwę typu lub nazwa jest kwalifikowana przez słowo kluczowe typename.

Istnieje wiele nazw, dla których {[17] } nie jest konieczne, ponieważ kompilator może, z odpowiednim wyszukiwaniem nazw w szablonie definicja, dowiedzieć się, jak parsować samego konstruktu - na przykład z T *f;, gdy T jest parametrem szablonu typu. Aby jednak t::x * f; była deklaracją, musi być zapisana jako typename t::x *f;. Jeśli pominie się słowo kluczowe, a nazwa zostanie uznana za nie-type, ale gdy instancja znajdzie, że oznacza typ, kompilator emituje zwykłe komunikaty o błędach. Czasami błąd jest podany w czasie definicji:

// t::x is taken as non-type, but as an expression the following misses an
// operator between the two names or a semicolon separating them.
t::x f;

składnia pozwala typename tylko przed zakwalifikowaniem nazwy - dlatego przyjmuje się, że nazwy niewykwalifikowane zawsze odnoszą się do typów, jeśli tak czynią.

Podobna sytuacja występuje w przypadku nazw oznaczających szablony, na co wskazuje tekst wprowadzający.

Słowo kluczowe "szablon"

Pamiętasz wstępny cytat powyżej i jak Standard wymaga specjalnej obsługi szablonów, jak również? Weźmy następujący niewinnie wyglądający przykład:

boost::function< int() > f;
To może wydawać się oczywiste dla ludzkiego czytelnika. Nie więc dla kompilatora. Wyobraź sobie następującą arbitralną definicję boost::function i f:
namespace boost { int function = 0; }
int main() { 
  int f = 0;
  boost::function< int() > f; 
}

To właściwie poprawne wyrażenie ! Używa operatora less-than do porównania boost::function z zerem (int()), a następnie używa operatora greater-than do porównania wynikowego bool z f. Jednak jak dobrze wiesz, boost::function w rzeczywistości jest szablonem, więc kompilator wie (14.2/3):

Po name lookup (3.4) stwierdzi, że nazwa jest template-name, jeśli po tej nazwie następuje

Teraz wracamy do tego samego problemu co z typename. Co jeśli nie możemy jeszcze wiedzieć, czy nazwa jest szablonem podczas parsowania kodu? Będziemy musieli wstawić template bezpośrednio przed nazwą szablonu, jak określono przez 14.2/4. Wygląda to tak:

t::template f<int>(); // call a function template

Nazwy szablonów mogą występować nie tylko po :: ale także po -> lub . w dostępie członka klasy. Musisz też wstawić słowo kluczowe:

this->template f<int>(); // call a function template

Zależności

Dla osób, które mają grube Standardowe książki na półce i które chcą wiedzieć, o czym dokładnie mówiłem, opowiem trochę o tym, jak to jest określone w standardzie.

W deklaracjach szablonu niektóre konstrukcje mają różne znaczenia w zależności od tego, jakich argumentów szablonu używasz do tworzenia instancji szablonu: Wyrażenia mogą mieć różne typy lub wartości, zmienne mogą mieć różne typy lub wywołania funkcji mogą zakończyć się wywołaniem różnych funkcji. O takich konstrukcjach zazwyczaj mówi się, że zależą od parametrów szablonu.

Norma określa dokładnie Zasady, czy dany konstruktor jest zależny, czy nie. Dzieli je na logicznie różne grupy: jedna łapie typy, druga łapie wyrażenia. Wyrażenia mogą zależeć od ich wartości i/lub typu. Więc mamy, z przykłady typowe:

  • typy zależne (np. parametr szablonu typu T)
  • wyrażenia zależne od wartości (np.: nie-typowy parametr szablonu N)
  • wyrażenia zależne od typu (np. rzut do parametru szablonu typu (T)0)

Większość reguł jest intuicyjna i budowana rekurencyjnie: na przykład Typ zbudowany jako T[N] jest typem zależnym, jeśli {[38] } jest wyrażeniem zależnym od wartości lub T jest zależnym Typ. Szczegóły tego można przeczytać w sekcji (14.6.2/1) dla typów zależnych, {[44] } dla wyrażeń zależnych od typu I (14.6.2.3) dla wyrażeń zależnych od wartości.

Nazwy zależne

Standard jest nieco niejasny co dokładnie jest zależną nazwą . Na prostym odczycie (wiesz, zasada najmniejszego zaskoczenia), wszystko, co definiuje jako nazwa zależna jest specjalnym przypadkiem dla nazw funkcji poniżej. Ale ponieważ wyraźnie T::x również musi być patrząc w kontekście instancjacji, musi to być również nazwa zależna (na szczęście od połowy C++14 Komitet zaczął szukać, jak naprawić tę mylącą definicję).

Aby uniknąć tego problemu, uciekłem się do prostej interpretacji tekstu standardowego. Spośród wszystkich konstrukcji oznaczających typy zależne lub wyrażenia, podzbiór z nich reprezentuje nazwy. Nazwy te są więc "nazwami zależnymi". Nazwa może przybierać różne formy - Standard says:

W przypadku, gdy nazwa jest używana jako identyfikator (2.11), operator-function-id (13.5), conversion-function-id (12.3.2) lub template-id (14.2), który oznacza podmiot lub etykietę (6.6.4, 6.1)

Identyfikator jest zwykłym ciągiem znaków / cyfr, podczas gdy dwa następne to formularze operator + i operator type. Ostatnia forma to template-name <argument list>. Wszystkie te nazwy są nazwami, a przez konwencjonalne użycie w standardzie, nazwa może również zawierać kwalifikatory, które mówią, jaka przestrzeń nazw lub nazwa klasy A powinien być sprawdzony.

Wyrażenie zależne od wartości 1 + N nie jest nazwą, ale N jest. Podzbiór wszystkich konstruktów zależnych będących nazwami nazywa się nazwą zależną . Nazwy funkcji mogą jednak mieć różne znaczenie w różnych instancjach szablonu, ale niestety nie są objęte tą ogólną zasadą.

Zależne nazwy funkcji

Nie chodzi przede wszystkim o ten artykuł, ale nadal warto wspomnieć: nazwy funkcji są wyjątki, które są obsługiwane oddzielnie. Nazwa funkcji identyfikatora jest zależna nie sama w sobie, ale od wyrażeń argumentów zależnych od typu użytych w wywołaniu. W przykładzie f((T)0), f jest nazwą zależną. W standardzie jest to określone w (14.6.2/1).

Dodatkowe uwagi i przykłady

W wystarczającym stopniu potrzebujemy zarówno typename, jak i template. Twój kod powinien wyglądać następująco:

template <typename T, typename Tail>
struct UnionNode : public Tail {
    // ...
    template<typename U> struct inUnion {
        typedef typename Tail::template inUnion<U> dummy;
    };
    // ...
};

Słowo kluczowe template nie zawsze musi pojawić się w ostatniej części nazwisko. Może pojawić się na środku przed nazwą klasy, która jest używana jako zakres, jak w poniższym przykładzie

typename t::template iterator<int>::value_type v;
W niektórych przypadkach słowa kluczowe są zabronione, jak opisano poniżej]}
  • Na nazwie zależnej klasy bazowej nie wolno pisać typename. Zakłada się, że podana nazwa jest nazwą typu klasy. Jest to prawdą dla obu nazw z listy klasy bazowej i listy inicjalizatorów konstruktora:

     template <typename T>
     struct derive_from_Has_type : /* typename */ SomeBase<T>::type 
     { };
    
  • W using-declarations to nie można użyć template po ostatnim ::, a Komitet C++ powiedział, aby nie pracować nad rozwiązaniem.

     template <typename T>
     struct derive_from_Has_type : SomeBase<T> {
        using SomeBase<T>::template type; // error
        using typename SomeBase<T>::type; // typename *is* allowed
     };
    
 953
Author: Johannes Schaub - litb,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2016-09-02 15:50:24

C++11

Problem

W C++03 Zasady dotyczące tego, kiedy potrzebujesz typename i template są w dużej mierze rozsądne, jest jedna irytująca wada jego sformułowania
template<typename T>
struct A {
  typedef int result_type;

  void f() {
    // error, "this" is dependent, "template" keyword needed
    this->g<float>();

    // OK
    g<float>();

    // error, "A<T>" is dependent, "typename" keyword needed
    A<T>::result_type n1;

    // OK
    result_type n2; 
  }

  template<typename U>
  void g();
};

Jak widać, potrzebujemy słowa kluczowego disambiguation, nawet jeśli kompilator potrafi sam się doskonale zorientować, że A::result_type może być tylko int (a więc jest typem), a {[9] } może być tylko szablonem member g zadeklarowanym później (nawet jeśli A jest gdzieś wyraźnie wyspecjalizowany, to nie będzie to wpływaj na kod w tym szablonie, więc na jego znaczenie nie może mieć wpływ późniejsza specjalizacja A!).

Obecna instancja

Aby poprawić sytuację, w C++11 język śledzi, gdy typ odnosi się do załączającego szablonu. Aby to wiedzieć, rodzaj musi być utworzony przez użycie określonej formy nazwy, która jest nazwą własną (w powyższym, A, A<T>, ::A<T>). Typ, do którego odnosi się taka nazwa, jest znany jako current instantiation . Mogą być wieloma typami, które są wszystkimi bieżącymi instancjami, jeśli typ, z którego została utworzona nazwa, jest klasą składową / zagnieżdżoną (wtedy A::NestedClass i {[11] } są bieżącymi instancjami).

Opierając się na tym pojęciu, język mówi, że CurrentInstantiation::Foo, Foo i CurrentInstantiationTyped->Foo (takie jak A *a = this; a->Foo) są wszystkie członkami bieżącej instancji jeśli są członkami klasy, która jest bieżącą instancją lub jedną z jej nie zależnych klas bazowych (wykonując tylko nazwę wyszukiwanie od razu).

Słowa kluczowe typename i template nie są już wymagane, jeśli kwalifikator jest członkiem bieżącej instancji. Należy pamiętać, że A<T> jest nadal nazwą zależną od typu (w końcu {[25] } jest również zależna od typu). Ale A<T>::result_type jest znany jako typ - kompilator" magicznie " zajrzy do tego rodzaju zależnych typów, aby to zrozumieć.

struct B {
  typedef int result_type;
};

template<typename T>
struct C { }; // could be specialized!

template<typename T>
struct D : B, C<T> {
  void f() {
    // OK, member of current instantiation!
    // A::result_type is not dependent: int
    D::result_type r1;

    // error, not a member of the current instantiation
    D::questionable_type r2;

    // OK for now - relying on C<T> to provide it
    // But not a member of the current instantiation
    typename D::questionable_type r3;        
  }
};
To imponujące, ale czy stać nas na więcej? Język idzie nawet dalej i wymaga , aby implementacja ponownie wyszukała D::result_type podczas tworzenia instancji D::f (nawet jeśli znalazła swoje znaczenie już w czasie definiowania). Gdy TERAZ wynik wyszukiwania różni się lub daje niejednoznaczność, program jest źle uformowany i należy podać diagnostykę. Wyobraź sobie, co się stanie, jeśli zdefiniujemy C w ten sposób
template<>
struct C<int> {
  typedef bool result_type;
  typedef int questionable_type;
};

Kompilator jest wymagany do wychwycenia błędu podczas tworzenia instancji D<int>::f. Więc masz najlepsze z dwóch światów: "opóźnione" lookup chroniąc cię, jeśli możesz wpadnij w kłopoty z zależnymi klasami bazowymi, a także" natychmiastowym " wyszukiwaniem, które uwalnia cię od typename i template.

Nieznane specjalizacje

W kodzie D nazwa typename D::questionable_type nie jest członkiem bieżącej instancji. Zamiast tego język oznacza go jako członka nieznanej specjalizacji. W szczególności jest tak zawsze, gdy wykonujesz DependentTypeName::Foo lub DependentTypedName->Foo i albo typem zależnym jest , a nie bieżąca instancja (w takim przypadku kompilator może zrezygnować i powiedzieć: "przyjrzymy się później, czym jest Foo) lub to jest bieżąca instancja i nazwa nie została znaleziona w niej lub jej nie zależnych klasach bazowych i istnieją również zależne klasy bazowe.

Wyobraź sobie, co się stanie, jeśli będziemy mieli funkcję member h Wewnątrz wyżej zdefiniowanego A szablonu klasy

void h() {
  typename A<T>::questionable_type x;
}

W C++03 język pozwalał wychwycić ten błąd, ponieważ nigdy nie było poprawnego sposobu na utworzenie instancji A<T>::h (niezależnie od argumentu give to T). W C++11 język ma teraz kolejną kontrolę, aby dać więcej powodów dla kompilatorów do implementacji tej reguły. Ponieważ A nie ma zależnych klas bazowych i A nie deklaruje żadnego członka questionable_type, nazwa A<T>::questionable_type nie jestani członkiem bieżącej instancji , ani członkiem nieznanej specjalizacji. W takim przypadku nie powinno być możliwości, aby ten kod mógł poprawnie skompilować się w czasie tworzenia instancji, więc język zabrania nazwy, w której kwalifikatorem jest bieżący instancja nie jest członkiem nieznanej specjalizacji ani członkiem aktualnej instancji (jednak naruszenie to nadal nie jest wymagane do rozpoznania).

Przykłady i ciekawostki

Możesz wypróbować tę wiedzę na tej odpowiedzi i sprawdzić, czy powyższe definicje mają dla ciebie sens na prawdziwym przykładzie (są one powtarzane nieco mniej szczegółowo w tej odpowiedzi).

Reguły C++11 sprawiają, że poniższy poprawny kod C++03 jest nieprawidłowy (co nie było zamierzone przez Komitet C++, ale prawdopodobnie nie zostanie naprawione)

struct B { void f(); };
struct A : virtual B { void f(); };

template<typename T>
struct C : virtual B, T {
  void g() { this->f(); }
};

int main() { 
  C<A> c; c.g(); 
}

Ten poprawny kod C++03 wiązałby this->f do A::f w czasie tworzenia instancji i wszystko jest w porządku. C++11 jednak natychmiast wiąże go z B::f i wymaga podwójnego sprawdzenia podczas tworzenia instancji, sprawdzając, czy odnośnik nadal pasuje. Jednak podczas tworzenia instancji C<A>::g, stosuje się regułę dominacji i lookup znajdzie A::f.

 124
Author: Johannes Schaub - litb,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-05-23 12:18:22

Przedmowa

[[34]}ten post ma być łatwą do odczytania alternatywą dla posta litba .

Podstawowy cel jest taki sam; wyjaśnienie do "kiedy?"i" dlaczego?"typename i template muszą być stosowane.


Jaki jest cel typename i template?

typename i template są użyteczne w okolicznościach innych niż przy deklarowaniu szablonu.

Istnieją pewne konteksty w C++ gdzie kompilator musi być wyraźnie poinformowany, jak traktować nazwę, a wszystkie te konteksty mają jedną wspólną cechę; zależą od co najmniej jednego template-parameter .

Odnosimy się do takich nazw, w których może występować niejednoznaczność w interpretacji, jak; " nazwy zależne ".

Ten post oferuje Wyjaśnienie zależności między zależnymi nazwami , a dwoma słowami kluczowymi.


FRAGMENT MÓWI PONAD 1000 SŁÓW

Spróbuj wyjaśnij, co dzieje się w poniższym function-template , samemu sobie, przyjacielowi, a może kotowi; co dzieje się w wyrażeniu oznaczonym (A )?

template<class T> void f_tmpl () { T::foo * x; /* <-- (A) */ }


Może to nie być takie proste, jak się wydaje, a dokładniej wynik oceny (A) w dużym stopniu zależy od definicji typu przekazanego jako parametr szablonu T.

Różne Ts mogą drastycznie zmienić semantykę zaangażowany.

struct X { typedef int       foo;       }; /* (C) --> */ f_tmpl<X> ();
struct Y { static  int const foo = 123; }; /* (D) --> */ f_tmpl<Y> ();


Dwa różne scenariusze :

  • Jeśli utworzymy szablon funkcji z typem X , tak jak w ( C), będziemy mieli deklarację wskaźnika-do into nazwie x, ale;

  • Jeśli tworzymy szablon z typem Y , jak w (D), (A ) składa się z wyrażenia, które oblicza iloczyn 123 pomnożona przez pewną już zadeklarowaną zmienną x .



UZASADNIENIE

Standard C++ dba o nasze bezpieczeństwo i Dobre Samopoczucie, przynajmniej w tym przypadku.

Aby zapobiec potencjalnym niespodziankom w implementacji, Standard nakazuje uporządkowanie niejednoznaczności zależnej Nazwy przez jawnie stwierdzając intencję wszędzie, gdzie chcielibyśmy traktować nazwę jako a type-name , or a template-id .

Jeśli nic nie jest podane, nazwa zależna będzie uważana za zmienną lub funkcję.



JAK OBCHODZIĆ SIĘ ZZALEŻNYMI NAZWAMI ?

Gdyby to był hollywoodzki film, nazwa zależna byłaby chorobą, która rozprzestrzenia się poprzez kontakt z ciałem, natychmiast wpływa na nosiciela, aby go zdezorientować. Zamieszanie, które może prowadzić do źle uformowanego perso -, erhm.. program.

A nazwa zależna to dowolna nazwa , która bezpośrednio lub pośrednio zależy od parametru szablonu .

template<class T> void g_tmpl () {
   SomeTrait<T>::type                   foo; // (E), ill-formed
   SomeTrait<T>::NestedTrait<int>::type bar; // (F), ill-formed
   foo.data<int> ();                         // (G), ill-formed    
}

Mamy cztery zależne nazwy w powyższym fragmencie:

  • E )
    • "typ" zależy od instancji SomeTrait<T>, które obejmują T, oraz;
  • F )
    • "NestedTrait" , który jest szablon-id , zależy od SomeTrait<T>, oraz;
    • "typ"na końcu ( F ) zależy od NestedTrait , który zależy od SomeTrait<T>, oraz;
  • G )
    • "data" , który wygląda jak szablon member-function , jest pośrednio zależną nazwą , ponieważ Typ foo zależy od instancji SomeTrait<T>.

Żaden z statement (E), (F) lub ( G ) jest poprawne, jeśli kompilator zinterpretuje zależne-Nazwy jako zmienne/funkcje (co, jak wspomniano wcześniej, dzieje się, jeśli nie mówimy wprost inaczej).

ROZWIĄZANIE

Aby g_tmplmiała poprawną definicję, musimy wyraźnie powiedzieć kompilatorowi, że oczekujemy typu in ( E), a template-idi a typein ( F) oraz template-idin ( G ).

template<class T> void g_tmpl () {
   typename SomeTrait<T>::type foo;                            // (G), legal
   typename SomeTrait<T>::template NestedTrait<int>::type bar; // (H), legal
   foo.template data<int> ();                                  // (I), legal
}

Za każdym razem gdy nazwa oznacza typ, wszystkie Nazwy muszą być albo type-names albo namespaces , mając to na uwadze, łatwo zauważyć, że stosujemy typename na początku naszej W pełni kwalifikowanej nazwy.

template Jednak jest inaczej pod tym względem, ponieważ nie ma sposobu, aby dojść do wniosku, takiego jak; " Och, to jest szablon, niż ten inny rzecz musi być również szablonem " . Oznacza to, że stosujemy template bezpośrednio przed dowolną nazwą, którą chcielibyśmy traktować jako taką.



CZY MOGĘ PO PROSTU UMIEŚCIĆ SŁOWA KLUCZOWE PRZED DOWOLNĄ NAZWĄ?

"Czy mogę trzymać typename i template przed jakimkolwiek imieniem? Nie chcę się martwić kontekstem, w jakim się pojawiają..." - Some C++ Developer

Reguły w standardzie mówią, że możesz zastosować słowa kluczowe tak długo, jak masz do czynienia z kwalifikowanym-imieniem (K), ale jeśli nazwa nie jest kwalifikowana Aplikacja jest źle utworzona ( L ).

namespace N {
  template<class T>
  struct X { };
}

         N::         X<int> a; // ...  legal
typename N::template X<int> b; // (K), legal
typename template    X<int> c; // (L), ill-formed

Uwaga : stosowanie typename lub template w kontekście, w którym nie jest to wymagane, nie jest uważane za dobrą praktykę; to, że możesz coś zrobić, nie oznacza, że powinieneś.


Dodatkowo istnieją konteksty gdzie typename i templatejawnie wyłączone:

  • Kiedy określamy bazy, których klasa dziedziczy

    Każda nazwa zapisana w liście typów pochodnych base-specifier-list {[40] } jest już traktowana jako nazwa typu , jawnie określająca typename jest zarówno źle uformowana, jak i zbędna.

                       // .------- the base-specifier-list
     template<class T> // v
     struct Derived      : typename SomeTrait<T>::type /* <- ill-formed */ {
       ...
     };
    


  • Gdy template-id jest tym, do którego odnosi się klasa pochodna using-directive

     struct Base {
       template<class T>
       struct type { };
     };
    
     struct Derived : Base {
       using Base::template type; // ill-formed
       using Base::type;          // legal
     };
    
 78
Author: Filip Roséen - refp,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-05-23 11:47:22
typedef typename Tail::inUnion<U> dummy;

Jednak nie jestem pewien, czy implementacja inUnion jest poprawna. Jeśli dobrze rozumiem, ta klasa nie powinna być tworzona jako instancja, dlatego zakładka "fail"nigdy nie zawiedzie. Może lepiej byłoby wskazać, czy typ jest w Unii, czy nie za pomocą prostej wartości logicznej.

template <typename T, typename TypeList> struct Contains;

template <typename T, typename Head, typename Tail>
struct Contains<T, UnionNode<Head, Tail> >
{
    enum { result = Contains<T, Tail>::result };
};

template <typename T, typename Tail>
struct Contains<T, UnionNode<T, Tail> >
{
    enum { result = true };
};

template <typename T>
struct Contains<T, void>
{
    enum { result = false };
};

PS: spójrz na Boost:: Variant

[[2]} PS2: spójrz na typelisty , szczególnie w książce Andrei Alexandrescu: Modern C++ Design
 19
Author: Luc Touraille,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2009-03-04 13:37:47

ta odpowiedź ma być dość krótka i słodka, aby odpowiedzieć (część) tytułowego pytania. Jeśli chcesz uzyskać bardziej szczegółową odpowiedź, która wyjaśnia, dlaczego musisz je tam umieścić, idź tutaj .


Ogólna zasada wprowadzania słowa kluczowego typename jest najczęściej wtedy, gdy używasz parametru szablonu i chcesz uzyskać dostęp do zagnieżdżonego typedef lub używając-aliasu, na przykład:

template<typename T>
struct test {
    using type = T; // no typename required
    using underlying_type = typename T::type // typename required
};

Zauważ, że dotyczy to również funkcji meta lub rzeczy, które przyjmują ogólne parametry szablonu. Jeśli jednak Podany parametr szablonu jest jawnym typem, nie musisz podawać typename, na przykład:

template<typename T>
struct test {
    // typename required
    using type = typename std::conditional<true, const T&, T&&>::type;
    // no typename required
    using integer = std::conditional<true, int, float>::type;
};

Ogólne zasady dodawania kwalifikatora template są w większości podobne, z wyjątkiem tego, że zazwyczaj dotyczą szablonowych funkcji składowych (statycznych lub innych) struktury / klasy, która sama jest szablonowa, na przykład:

Biorąc pod uwagę tę strukturę i funkcję:

template<typename T>
struct test {
    template<typename U>
    void get() const {
        std::cout << "get\n";
    }
};

template<typename T>
void func(const test<T>& t) {
    t.get<int>(); // error
}

Próba dostępu t.get<int>() z wnętrza funkcja spowoduje błąd:

main.cpp:13:11: error: expected primary-expression before 'int'
     t.get<int>();
           ^
main.cpp:13:11: error: expected ';' before 'int'

Tak więc w tym kontekście należy wcześniej użyć słowa kluczowego template i nazwać go w ten sposób:

t.template get<int>()

W ten sposób kompilator przetworzy to poprawnie, a nie t.get < int.

 16
Author: Rapptz,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-05-23 12:10:34

Umieszczam doskonałą odpowiedź JLBorges na podobne pytanie od cplusplus.com, ponieważ jest to najbardziej zwięzłe wyjaśnienie, jakie czytałem na ten temat.

W szablonie, który piszemy, istnieją dwa rodzaje nazw, które mogą być użyte - nazwy zależne i nazwy niezależne. Nazwa zależna to nazwa zależna od parametru szablonu; nazwa zależna ma takie samo znaczenie niezależnie od parametrów szablonu.

Dla przykład:

template< typename T > void foo( T& x, std::string str, int count )
{
    // these names are looked up during the second phase
    // when foo is instantiated and the type T is known
    x.size(); // dependant name (non-type)
    T::instance_count ; // dependant name (non-type)
    typename T::iterator i ; // dependant name (type)

    // during the first phase, 
    // T::instance_count is treated as a non-type (this is the default)
    // the typename keyword specifies that T::iterator is to be treated as a type.

    // these names are looked up during the first phase
    std::string::size_type s ; // non-dependant name (type)
    std::string::npos ; // non-dependant name (non-type)
    str.empty() ; // non-dependant name (non-type)
    count ; // non-dependant name (non-type)
}

To, do czego odnosi się nazwa zależna, może być czymś innym dla każdej innej instancji szablonu. W związku z tym szablony C++ podlegają "dwufazowemu wyszukiwaniu nazw". Gdy szablon jest wstępnie parsowany (przed jakąkolwiek instancją), kompilator wyszukuje nazwy nie zależne. Gdy dana instancja szablonu ma miejsce, parametry szablonu są znane do tego czasu, a kompilator wyszukuje nazwy zależne.

Podczas pierwszej fazy parser musi wiedzieć, czy nazwa zależna jest nazwą typu, czy nazwą Nie-typu. Domyślnie przyjmuje się, że nazwa zależna jest nazwą typu innego niż typ. Słowo kluczowe typename przed nazwą zależną określa, że jest to nazwa typu.


Podsumowanie

Używaj słowa kluczowego typename tylko w deklaracjach i definicjach szablonu, pod warunkiem, że masz kwalifikowaną nazwę, która odnosi się do typu i zależy od szablonu parametr.

 2
Author: Nik-Lz,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2018-08-11 12:20:50