13

Podczas czytania this question znalazłem dziwny punkt:C++ przy użyciu deklaracji z TypeName w dziedziczących-konstruktorów

template <typename T> 
class Subclass : public Baseclass<T> 
{ 
public: 
    using typename Baseclass<T>::Baseclass; 
    // ^^^^^^^^ 
}; 

Od typename, Baseclass<T>::Baseclass powinny być injected class name, a nie konstruktor. O ile mi wiadomo, jest to ten sam przypadek:

template <typename T> 
class Base 
{ 
public: 
    typedef short some_type; 
}; 

template <typename T> 
class Sub : public Base<T> 
{ 
public: 
    using typename Base<T>::some_type; 
}; 

Aby się upewnić, napisałem kod testowy.

#include <iostream> 

template <typename T> 
class Base 
{ 
public: 
    Base() { std::cout << "A::A()\n"; } 
    Base(int) { std::cout << "A::A(int)\n"; } 
    Base(const char *) { std::cout << "A::A(const char *)\n"; } 
}; 

template <typename T> 
class Sub : public Base<T> 
{ 
    using typename Base<T>::Base; 
}; 

int main() 
{ 
    Sub<char> s1; 
    Sub<char> s2(3); 
    Sub<char> s3("asdf"); 
} 

Jednak działa na gcc 4.8.3. To jest również bez

$ cat test.cpp 
... 
    using Base<T>::Base; 
... 

$ g++ -std=c++1y -Wall -Wextra -Werror -pedantic test.cpp -o test && ./test 
A::A() 
A::A(int) 
A::A(const char *) 

Dlaczego otrzymałem te wyniki? Co mnie ominęło?

+3

dzyń ++ odrzuca ' typename ". – dyp

+5

Za bardzo dbałem o to, by moja zdrowa psychika mogła odpowiedzieć na to pytanie w innym pytaniu ... Standard mówi w [namespace.udecl]/1 "Jeśli * using-declaration * nazywa konstruktor, domyślnie deklaruje zestaw konstruktorów w klasa, w której pojawia się * use-declaration *, w przeciwnym razie nazwa podana w * use-declaration * jest synonimem nazwy jakiegoś encji zadeklarowanej gdzie indziej. " Ale w [klasa.ktor]/1 "Konstruktory nie mają nazw." – dyp

+1

Należy zauważyć, że istnieje [przestrzeń nazw.udecl]/20 "Jeśli * using-declaration * używa słowa kluczowego' typename' i określa zależną nazwę, nazwa wprowadzona przez * using-declaration * jest traktowana jako * typedef-name *. " – dyp

Odpowiedz

1

Standardem jest to całkiem jasne, ([namespace.udecl]/1)

using-zgłoszenia:

pomocą typename_opt zagnieżdżony nazw specyfikatora bezwarunkowego-id;

typename kluczowe jest zatem opcjonalnie częścią używając deklaracji, które mogą pojawić się nawet przy użyciu oświadczeń niebędących typów. Poniższy kod powinien być zatem norma conformant:

template < typename T > class Base { 
    protected: 
    typedef T Ttype; 
    Ttype member; 

    public: 
    Base() { 
     std::cout << "A::A()\n"; 
    } 
    Base(int) { 
     std::cout << "A::A(int)\n"; 
    } 
    Base(const char *) { 
     std::cout << "A::A(const char *)\n"; 
    } 

    protected: 
    void memfunc(void) { 
     std::cout << "A::memfunc(void)\n"; 
    } 
}; 

template< typename T > 
struct SubNoTypename : protected Base<T> { 
    using Base<T>::Base; 
    using Base<T>::member; 
    using Base<T>::memfunc; 
    using Base<T>::Ttype; // n.b. no error in clang++ 
}; 

template< typename T > 
struct SubTypename : protected Base<T> { 
    using typename Base<T>::Base; // error in clang++ 
    using typename Base<T>::member; // error in clang++ 
    using typename Base<T>::memfunc; // error in clang++ 
    using typename Base<T>::Ttype; 
}; 

Zarówno SubNoTypename i SubTypename są uznawane za standardowe conformant przez GCC. Z drugiej strony clang ++ narzeka w SubTypename na słowa kluczowe o malpacku typename. Nie jest to jednak spójne, ponieważ powinno wtedy narzekać na brakujące typename w using Base<T>::Ttype;. To wyraźnie jest błąd klang.


Edit typename kluczowe jest również dozwolone, jeśli klasa bazowa ma klasy szablon, miejsce, gdzie zwykle nigdy nie spodziewałbym się tego słowa kluczowego, aby być ważne:

class BaseNoTemplate { 
    protected: 
    typedef T Ttype; 
    Ttype member; 

    public: 
    BaseNoTemplate() { 
     std::cout << "A::A()\n"; 
    } 
    BaseNoTemplate(const char *) { 
     std::cout << "A::A(const char *)\n"; 
    } 

    void memfunc(void) { 
     std::cout << "A::memfunc(void)\n"; 
    } 
}; 

struct SubNoTemplateNoTypename : protected BaseNoTemplate { 
    using BaseNoTemplate::BaseNoTemplate; 
    using BaseNoTemplate::member; 
    using BaseNoTemplate::memfunc; 
    using BaseNoTemplate::Ttype; 
}; 

struct SubNoTemplateTypename : protected BaseNoTemplate { 
    using typename BaseNoTemplate::BaseNoTemplate; // error in clang++ 
    using typename BaseNoTemplate::member; // error in clang++ 
    using typename BaseNoTemplate::memfunc; // error in clang++ 
    using typename BaseNoTemplate::Ttype; // n.b. no error in clang++ 
}; 
+0

Dziękuję za odpowiedź na moje stare pytanie bez odpowiedzi! Problem został rozwiązany, ale nie mogę przestać się zastanawiać * dlaczego * standard pozwala na * typename_opt * wewnątrz deklaracji 'using' - wydaje mi się niepotrzebne, myślę :) – ikh

+1

Przepraszam, ale to nie ma sensu. Fakt, że gramatyka dopuszcza słowo kluczowe "nazwa_pliku" w przypadku typów niebędących typami, jest po prostu tym, że ograniczenie typu "nazwa_pliku" jest używane w odniesieniu do typów, jest raczej semantycznym ograniczeniem, a nie składniowym. (Ale jeśli zajmiesz się również semantycznymi ograniczeniami, jeśli w ogóle, może to stanowić dobrą odpowiedź.) – hvd

+0

@hvd Dodałem kilka przykładów pokazujących użycie typename dla nie-szablonowych klas bazowych. Imho to pytanie syntaktyczne, a nie semantyczne. – user1978011

0

Przepraszam, dlaczego nie chcesz using, dlaczego nie tylko typedef?

template <typename T> 
class Sub : public Base<T> 
{ 
    typedef Base<T> Base; 
}; 
+2

Powinieneś wyszukać o "C++ dziedzicząc konstruktorów" :) – ikh