13

Во время чтения this question, я обнаружил странную точку:C++ с использованием декларации с TypeName в наследующих-конструкторах

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

typename С, Baseclass<T>::Baseclass должен быть injected class name, не конструктор. Насколько мне известно, это тот же случай:

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

Чтобы убедиться, что я написал тестовый код.

#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"); 
} 

Однако, он работает на gcc 4.8.3.

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

Он также работает без typename.

$ 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 *) 

Почему я получу эти результаты? Что я упустил?

+3

лязг ++ отвергает ' typename'. – dyp

+5

Я слишком заботился о своем здравомыслии, чтобы ответить на этот вопрос по другому вопросу ... Стандарт говорит в [namespace.udecl]/1 «Если * use-declaration * указывает на конструктор, он неявно объявляет набор конструкторов в класс, в котором появляется выражение * using-declaration *, в противном случае имя, указанное в * using-declaration *, является синонимом имени какого-либо объекта, объявленного в другом месте. " Но в [class.ctor]/1 «Конструкторы не имеют имен». – dyp

+1

Обратите внимание, что существует [пространство имен.udecl]/20 "Если * using-declaration * использует ключевое слово' typename' и указывает зависимое имя, имя, введенное с помощью * using-declaration *, рассматривается как * typedef-name *. " – dyp

ответ

1

Стандарт довольно ясно говорят об этом ([namespace.udecl]/1)

использования декларация:

использования typename_opt вложенного-имени-спецификатор неквалифицированного идентификатор; Поэтому

typename ключевое слово является необязательным часть с помощью заявления, которое может появиться даже при использовании деклараций не-типов. Таким образом, следующий код должен быть стандартным совместимую:

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

Оба SubNoTypename и SubTypename признаны в качестве стандартного совместимую по НКУ. С другой стороны, clang ++ жалуется на SubTypename о незанятых typename ключевых словах. Однако это даже не согласовано, потому что тогда оно должно жаловаться на отсутствие typename в using Base<T>::Ttype;. Это, безусловно, ошибка.


Редактировать typename ключевое слово также допускается, если базовый класс не шаблонный класс, место, где обычно вы бы никогда не ожидаем, что это ключевое слово, чтобы быть действительным:

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

Спасибо за ваш ответ на мой старый вопрос без ответа! Проблема была решена, но я не могу не задаться вопросом, почему * standard допускает * typename_opt * внутри 'using' декларации - кажется, что это не нужно, я думаю :) – ikh

+1

Извините, но это не имеет никакого смысла. Тот факт, что грамматика допускает ключевое слово 'typename' для не-типов, просто потому, что ограничение, которое' typename' используется для типов, является семантическим ограничением, а не синтаксическим. (Но если вы также обращаетесь к семантическим ограничениям, если они есть, это может сделать хороший ответ.) – hvd

+0

@hvd Я добавил несколько примеров, показывающих использование typename для базовых классов без шаблонов. Имхо это синтаксический вопрос, а не семантический. – user1978011

0

Извините, зачем вы хотите using, почему бы не просто typedef?

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

Вам следует искать "C++ наследующие конструкторы" :) – ikh