Когда можно использовать `typename` с идентификаторами, которые однозначно ссылаются на тип? - PullRequest
3 голосов
/ 20 апреля 2019

Обычно typename используется для устранения неоднозначности между случаями, когда идентификатор может относиться к типу или может относиться к чему-то другому:

template<class T>
void foo(typename T::type value) {
   // ...
}

Когда можно использовать typename, когда идентификаторуже тип?

1. Можно ли его использовать, если класс с таким именем уже существует?

class MyClass{};

void foo(typename MyClass value) {}

2. Может ли ониспользоваться с параметром шаблона, объявленным как тип?

template<class T>
void foo(typename T value) {}

3. Может ли он использоваться с внутренними классами, которые однозначно являются типами?

class A {
   public:
    class B {};
};

// Compiles; no typename necessary
void foo(A::B value) {} 
// This compiles too on gcc and msvc
void bar(typename A::B value) {}

Интерпретация

Случай 1: MSVC считает, что это нормально;gcc и clang выдают ошибку

Случай 2: MSVC считает это нормально;gcc и clang выдают ошибку

Случай 3: A::B однозначно является типом, но gcc и clang теперь разрешают использовать typename.

Ответы [ 2 ]

1 голос
/ 18 мая 2019

Ключевое слово typename разрешено только синтаксисом C ++ для введения параметра типа шаблона или перед квалифицированным именем , то есть чем-то, содержащим :: токен.

То есть ваши # 1 и # 2 плохо сформированы, потому что MyClass и T являются неквалифицированными именами, не содержащими ::.

Перед квалифицированным именем токен typename:

C ++ 17 [temp.res] / 3,5,6:

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

Квалифицированное имя, используемое в качестве имени в class-or-decltype (Clause [class.derived]) или подробный спецификатор типа неявно предполагается для имени введите без использования ключевого слова typename. В спецификаторе вложенного имени , который непосредственно содержит спецификатор вложенного имени , который зависит от параметра шаблона, идентификатор или простой-шаблон -id подразумевается как имя типа без использования ключевого слова typename. [ Примечание: Ключевое слово typename запрещено синтаксисом этих конструкций. - Конечная нота ]

Если для заданного набора аргументов шаблона создается специализация шаблона, которая ссылается на qualid-id , который обозначает тип или шаблон класса, и qualid-id относится к члену неизвестной специализации, qualid-id должен либо иметь префикс typename, либо использоваться в контексте, в котором он неявно называет тип, как описано выше.

Таким образом, ваш # 3 правильно сформирован, даже если имя не зависит от параметра шаблона и даже не содержится в объявлении шаблона.

Примечание. C ++ 20 добавит еще много контекстов , где typename является необязательным, даже с зависимым именем, поскольку из контекстов можно однозначно определить, что имя может означать только тип.

1 голос
/ 20 апреля 2019

От cppreference

Использование:

  • В объявлении шаблона typename может использоваться в качестве альтернативы классу для объявления параметров шаблона типа и параметров шаблона (начиная с C ++ 17).
  • Внутри объявления или определения шаблона можно использовать typename, чтобы объявить, что зависимое имя является типом.
  • Внутри требования к требованиям типа (начиная с C ++ 20)

Так что я бы сказал, что у вас нет гарантий, что вариант 1 и вариант 2 будут скомпилированы. Поскольку они не попадают ни в один из этих трех вариантов использования.

А для случая 3 давайте посмотрим, что cppreference может сказать по этому поводу:

Ключевое слово typename может использоваться таким образом только перед квалифицированными именами (например, T :: x), но имена не должны быть зависимыми.

Ключевое слово typename должно использоваться только в объявлениях и определениях шаблонов и только в тех контекстах, в которых могут использоваться зависимые имена. Это исключает явные объявления специализации и явные объявления экземпляров. (До C ++ 11)

Ключевое слово typename может использоваться даже вне шаблонов. (начиная с C ++ 11)

Так как в вашем примере нет шаблона, гарантируется, что case 3 будет работать только с C ++ 11

И действительно, тестовая программа скомпилирована нормально с g ++ -std = c ++ 11 , но выдает это предупреждение без -std = c ++ 11

предупреждение: typename встречается вне шаблона [-Wc ++ 11-расширений]

...