When can `typename` be used with identifiers that unambiguously refer to a type?

六月ゝ 毕业季﹏ 提交于 2019-12-20 02:15:20

问题


Normally, typename is used to disambiguate between cases where an identifier may refer to a type, or may refer to something else:

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

When can typename be used when an identifier is already a type?

1. Can it be used if there's already a class with this name?

class MyClass{};

void foo(typename MyClass value) {}

2. Can it be used with a template parameter that's declared as a type?

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

3. Can it be used with inner classes that are unambiguously types?

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) {}

Compiler interpretation

Case 1: MSVC considers this OK; gcc and clang throw an error

Case 2: MSVC considers this OK; gcc and clang throw an error

Case 3: A::B is unambiguously a type, but gcc and clang now permit the use of typename.


回答1:


The keyword typename is only permitted by the C++ syntax to introduce a template type parameter, or before a qualified name, i.e. something containing the :: token.

So your #1 and #2 are ill-formed because MyClass and T are unqualified names, not containing any ::.

Before a qualified name, the typename token is:

  • not allowed by the grammar before a base class name in the head of a class definition, or in combination with the class, struct, or union keywords; a qualified name is always treated as a type in these contexts
  • otherwise required, if the qualified name is dependent and a member of an unknown specialization
  • otherwise optional, whether within a template declaration or not

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

When a qualified-id is intended to refer to a type that is not a member of the current instantiation and its nested-name-specifier refers to a dependent type, it shall be prefixed by the keyword typename, forming a typename-specifier. ...

A qualified name used as the name in a class-or-decltype (Clause [class.derived]) or an elaborated-type-specifier is implicitly assumed to name a type, without the use of the typename keyword. In a nested-name-specifier that immediately contains a nested-name-specifier that depends on a template parameter, the identifier or simple-template-id is implicitly assumed to name a type, without the use of the typename keyword. [Note: The typename keyword is not permitted by the syntax of these constructs. - end note]

If, for a given set of template arguments, a specialization of a template is instantiated that refers to a qualified-id that denotes a type or a class template, and the qualified-id refers to a member of an unknown specialization, the qualified-id shall either be prefixed by typename or shall be used in a context in which it implicitly names a type as described above.

So your #3 is well-formed, even though the name does not depend on a template parameter and isn't even in a template declaration.

Note C++20 will add many more contexts where typename is optional even with a dependent name, because it's possible to unambiguously determine from the contexts that the name can only mean a type.




回答2:


From cppreference

Usage:

  • In a template declaration, typename can be used as an alternative to class to declare type template parameters and template template parameters (since C++17).
  • Inside a declaration or a definition of a template, typename can be used to declare that a dependent name is a type.
  • Inside a requirements for type requirements (since C++20)

So I would say that you have no guaranties that case 1 and case 2 will compile. As they do not fall into any of these three usage cases.

And for case 3, let's see what cppreference has to say about that:

The keyword typename may only be used in this way before qualified names (e.g. T::x), but the names need not be dependent.

The keyword typename must only be used in template declarations and definitions and only in contexts in which dependent names can be used. This excludes explicit specialization declarations and explicit instantiation declarations.(until C++11)

The keyword typename can be used even outside of templates. (since C++11)

So as there is no template in your example, case 3 should be guarantied to work only since C++11

And indeed a test program compiled OK with g++ -std=c++11 but issued this warning without -std=c++11

warning: 'typename' occurs outside of a template [-Wc++11-extensions]



来源:https://stackoverflow.com/questions/55777503/when-can-typename-be-used-with-identifiers-that-unambiguously-refer-to-a-type

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!