Подтвердить что ты не робот

С++ с использованием объявления с typename в inheriting-constructors

При чтении этого вопроса я нашел странный момент:

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

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

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

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

4b9b3361

Ответ 1

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

с помощью декларирование:

с использованием typename_opt nested-name-specifier unqualified-id;

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

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 распознаются как стандартное соответствие gcc. С другой стороны, clang++ жалуется на SubTypename на неприменимые ключевые слова typename. Однако это даже не согласовано, потому что тогда оно должно жаловаться на отсутствие typename в using Base< T >::Ttype;. Это явно ошибка clang.


Edit Ключевое слово 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++
};

Ответ 2

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

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