我必须在何处以及为什么要放置“模板”。和“ typename”关键字?
在模板中,在何处以及为什么必须将typename
和模板
放在依赖名称上?
无论如何,依赖名称到底是什么?
我有以下代码:
template <typename T, typename Tail> // Tail will be a UnionNode too.
struct UnionNode : public Tail {
// ...
template<typename U> struct inUnion {
// Q: where to add typename/template here?
typedef Tail::inUnion<U> dummy;
};
template< > struct inUnion<T> { };
};
template <typename T> // For the last node Tn.
struct UnionNode<T, void> {
// ...
template<typename U> struct inUnion; // intentionally not defined
template< > struct inUnion<T> { }; // specialization only for T
};
我遇到的问题在于typedef tail :: Inunion&lt; u&gt;虚拟
行。我可以肯定的是,innunion
是一个因名称,而VC ++在cho住了它的位置很合适。
我还知道我应该能够在某个地方添加template
告诉编译器Inunion
是 template-id ,但是到底在哪里?然后,应该假设innuion
是类模板,即innunion&lt; u&gt;
name name name and andes not type而不是函数?
In templates, where and why do I have to put typename
and template
on dependent names?
What exactly are dependent names anyway?
I have the following code:
template <typename T, typename Tail> // Tail will be a UnionNode too.
struct UnionNode : public Tail {
// ...
template<typename U> struct inUnion {
// Q: where to add typename/template here?
typedef Tail::inUnion<U> dummy;
};
template< > struct inUnion<T> { };
};
template <typename T> // For the last node Tn.
struct UnionNode<T, void> {
// ...
template<typename U> struct inUnion; // intentionally not defined
template< > struct inUnion<T> { }; // specialization only for T
};
The problem I have is in the typedef Tail::inUnion<U> dummy
line. I'm fairly certain that inUnion
is a dependent name, and VC++ is quite right in choking on it.
I also know that I should be able to add template
somewhere to tell the compiler that inUnion
is a template-id, but where exactly? Should it then assume that inUnion
is a class template, i.e. inUnion<U>
names a type and not a function?
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(10)
C ++ 20和C ++ 23
对于感兴趣的人,我将从现代C ++的角度提供完整的答案。
C ++ 20和C ++ 23之间只有很小的差异。
为什么我们需要
模板
和typename
?基本问题是,没有其他提示,编译器无法理解C ++。正式地,C ++语法是上下文敏感的,您需要提供其他disamuguators。
如您所见,问题是
t
可以是任何类型,因此t ::类型
和t :: Templ
可以是任何东西:静态数据成员,类型别名,模板,成员函数模板,非模板成员函数等。这是因为
t :: Type
和t :: Templ
是相关类型,取决于模板参数t
。C ++通过将
t ::某物
作为数据成员或成员函数来解决这种歧义。您指定template
或typename
是被解释为类型或模板的东西。
依赖类型和表达式
概括术语的
t :: Type
之类的类型是依赖性模板参数t
。(x+y)
之类的表达 - 依赖性如果x
或y
依赖于模板类型参数。(x+y)
之类的表达 - 依赖性如果x
或y
依赖于非类型模板参数。通常,当涉及依赖类型时,
typename
template 是必要的。如果从依赖的表达式形成依赖类型,例如decltype(x + y)::类型
也可能发生这种情况。模板
和typename
关键字如何工作?这些关键字有时被称为Disamamutorator,并告知编译器您需要类型或模板而不是数据成员/成员函数。
typename
disambuatortypename
被准备到 typename特定词,并适用于需要歧义的名字。例如:template
disambiguatortemplate
在合格 - id ,使其如此&lt;
不是被解释为不太少的操作员,而是一个开始 template-argument-list 。如上一个示例所示,
typename
可以使模板
不必要,因为templ&lt;
只能将其解释为a template-argument-list 如果templ
必须是一种类型。模板
有另一种(现在已弃用)的使用,即提供template-template参数:此用途已被弃用,因为始终很明显
&lt; ...&gt;
是a template-argument-list 在此上下文中和模板
是毫无意义的。C ++ 20和C ++ 23放松是多少?
模板
和typename
的许多用途现已变得不必要。如上所述,typename
有时制作模板
不必要。更一般地,typename
和模板
可以省略仅类型的上下文。例如:
这些用途和其他用途将在C ++ 20之前被不良形成。 C ++ 23添加了
template
和typename
之间的交互,这消除了进一步的用途。相关建议是:
,通常会努力消除
typename
and code>和模板的所有位置
并不是真正必要的。请注意,
template
和typename
并不总是需要歧义,而只是有助于解析。例如:在这里,我们仍然需要
typename
,尽管明确是声明。否则,知道a
是一种类型和a ::
是有效的。因此,在这种情况下,
typename
不太可能成为可选的。C++20 and C++23
For those interested, I will provide complete answer from a modern C++ perspective.
There are only minor differences between C++20 and C++23.
Why do we need
template
andtypename
?The basic issue is that C++ can't be understood by the compiler without some additional hints. Formally, the C++ grammar is context-sensitive, and you need to provide additional disambiguators.
As you can see, the problem is that
T
can be any type, and soT::type
andT::templ
can be anything: static data members, type aliases, templates, member function templates, non-template member functions,etc.This is because
T::type
andT::templ
are dependent types, depending on the template parameterT
.C++ resolves this ambiguity by treating
T::something
as a data member or member function when in doubt. Only ifT
is known, or if you specifytemplate
ortypename
, issomething
interpreted as a type or template.Dependent types and expressions
To sum up the terminology:
T::type
is dependent on a template parameterT
.(x + y)
is type-dependent ifx
ory
depend on a template type parameter.(x + y)
is value-dependent ifx
ory
depend on a non-type template parameter.Generally,
typename
andtemplate
become necessary when dependent types are involved. This can also happen if a dependent type is formed from a dependent expression, such asdecltype(x + y)::type
.How do the
template
andtypename
keywords work?These keywords are sometimes called disambiguators, and inform the compiler that you want a type or a template instead of a data member/member function.
The
typename
disambiguatortypename
is prepended to a typename-specifier and applies to the first name that needs disambiguation. For example:The
template
disambiguatortemplate
acts as "glue" within a qualified-id which makes it so<
is not interpreted as the less-than operator, but the beginning of a template-argument-list.As seen in the last example,
typename
can maketemplate
unnecessary becausetempl<
can only be interpreted as a template-argument-list iftempl
must be a type.There is another (now deprecated) use of
template
, namely for providing template-template arguments:This use has been deprecated because it is always clear that
<...>
is a template-argument-list in this context, andtemplate
is pointless.What are the C++20 and C++23 relaxations?
Many uses of
template
andtypename
have now become unnecessary. As seen above,typename
sometimes makestemplate
unnecessary. More generally,typename
andtemplate
can be omitted in a type-only context.For example:
These and other uses would have been ill-formed prior to C++20. C++23 has added the interaction between
template
andtypename
which eliminates further uses.The relevant proposals are:
typename
! (C++20)In general, there is an effort to eliminate all the places where
typename
andtemplate
aren't truly necessary.Note that
template
andtypename
are not always necessary for disambiguation, but merely assist in parsing. For example:Here, we still need
typename
despite this unambiguously being a declaration. Otherwise, it would require arbitrarily look-ahead to know thata
is a type anda::
is valid.Therefore, it is unlikely that
typename
will ever become optional in this context.依赖名称是名称取决于模板参数,我们需要指示编译器以正确编译模板类/功能,然后才能实际进行分组。
打字机 - &gt;告诉编译器依赖性名称是实际类型
模板 - &gt;告诉编译器依赖名称是模板函数/class
Dependent name is a name depends on template parameters, we need to instruct compiler in order to compile the template class/function properly before actually instiatiate them.
typename -> tell compiler the dependent name is an actual type
template -> tell compiler the dependent name is a template function/class
我放置Jlborges的出色 wornge ,因为这是我阅读的有关该主题的最简洁的解释。
摘要
仅在模板声明和定义中使用关键字TypeName,前提是您具有指称类型的合格名称,并取决于模板参数。
I am placing JLBorges's excellent response to a similar question verbatim from cplusplus.com, as it is the most succinct explanation I've read on the subject.
Summary
Use the keyword typename only in template declarations and definitions provided you have a qualified name that refers to a type and depends on a template parameter.
Simple
You need it when you call a templated function from inside a templated class:
LiveDemo
Will output
from the templated
print()
function在可打印
中。Simple
You need it when you call a templated function from inside a templated class:
LiveDemo
Will output
from the templated
print()
function inprintable
.(请参阅在这里也为我的C ++ 11答案)
为了解析C ++程序,编译器是否需要知道是否需要知道某些名称是否类型。以下示例表明:
如何解析这一点?对于许多语言,编译器不需要知道名称的含义即可解析并基本上知道代码行执行的操作。在C ++中,以上可以根据
t
的含义产生截然不同的解释。如果是一种类型,则将是指针f
的声明。但是,如果不是类型,那将是乘法。因此,C ++标准在段(3/7)上说:编译器将如何找出名称
t :: x
是指,如果t
是指模板类型参数?X
可能是一个静态的INT数据成员,可以乘以或同样可以是嵌套类或类型的静态数据成员,可以屈服于声明。 如果一个名称具有此属性 - 直到已知实际的模板参数才能查找 - 则称为a 依赖性名称(“取决于”模板参数)。您可能建议您只等到用户实例化模板:
,这将起作用,并实际上由标准允许作为可能的实施方法。这些编译器基本上将模板的文本复制到内部缓冲区中,并且只有在需要实例化时,它们才会解析模板并可能检测到定义中的错误。但是,其他实施方式并没有用模板作者的错误来打扰模板的用户(可怜的同事!),而是选择尽早检查模板并在实例化发生之前尽快在定义中检查模板。
因此,必须有一种方法告诉编译器某些名称是类型,而某些名称则不是。
“打字”关键字
答案是:我们决定编译器应如何解析此内容。如果
t :: x
是一个因名称,则我们需要通过typename
前缀它来告诉编译器以某种方式解析它。该标准在(14.6/2)上说:有很多名称
typename
不需要,因为编译器可以在模板定义中使用适用的名称查找,找出如何解析构造本身 - 例如使用t * f;
,当t
是类型模板参数时。但是,对于t :: x * f;
要成为声明,必须将其写入typename t :: x * f;
。如果您省略了关键字,并且名称被视为非类型,但是当实例化发现它表示类型时,则编译器会发出通常的错误消息。有时,在定义时给出错误:语法允许
typeName
仅在合格名称之前,因此被认为是批准的,始终知道无限制的名称是参考类型如果他们这样做。正如介绍性文本所暗示的那样,对于表示模板的名称也存在类似的陷阱。
“模板”关键字
还记得上面的初始引用以及标准如何也需要对模板进行特殊处理?让我们以以下无辜的外观例子:
对于人类读者来说,这看起来可能很明显。编译器并非如此。想象一下,
boost :: function
和f
的以下任意定义实际上是有效的 expression !它使用较小的操作员比较
boost :: function
与零(int()
)进行比较,然后使用大于运算符比较结果bool
针对f
。但是,如您所知,boost :: function
在现实生活中是一个模板,因此编译器知道(14.2/3):现在,我们又回到了与
typename
的问题相同的问题。如果我们还不知道该名称是解析代码时的模板怎么办?我们需要在模板名称之前立即插入模板
,如14.2/4
所指定。这看起来像:模板名称不仅可以在
::
之后发生,还可以在类成员访问中的- &gt;
或。。。您还需要在此处插入关键字:依赖项
对于架子上有厚标准书籍并且想知道我到底在说什么的人们的
,我将谈论标准中如何指定这一点。在模板声明中,一些构造的含义不同,具体取决于您用于实例化模板的模板参数:表达式可能具有不同的类型或值,变量可能具有不同的类型或函数调用可能最终会调用不同的函数。这种构造通常被认为依赖对模板参数。
该标准通过构造是否取决于规则来精确定义规则。它将它们分为逻辑上不同的群体:一种捕获类型,另一种捕获表达式。表达可能取决于其价值和/或其类型。因此,我们有典型的示例附加:
t
)n
)(t)0
)大多数规则都是直观的,并且是递归构建的:例如,一种构造为
t [n的类型]
是一种因类型,如果n
是值依赖的表达式或t
是一个因类型。可以在(14.6.2/1
)部分中读取此详细信息,(14.6.2.2)
类型依赖性表达式和(14.6) .2.3)
用于值依赖的表达式。依赖名称
标准对的确切是依赖的名称。在简单的读取中(您知道,最小令人惊讶的原理),它将其定义为依赖性名称是下面函数名称的特殊情况。但是由于清楚地
t :: x
还需要在实例化环境中查找,因此它也需要是一个依赖的名称(幸运的是,从C ++ 14开始,委员会已经开始研究如何修复这个令人困惑的定义)。为了避免此问题,我诉诸于对标准文本的简单解释。在表示因类型或表达式的所有构造中,它们的一个子集表示名称。因此,这些名称是“依赖的名称”。名称可以采用不同的形式 - 标准说:
标识符只是字符/数字的普通序列,而接下来的两个是
operator +
和操作员类型
表单。最后一个形式是template-name&lt;参数列表&gt;
。所有这些都是名称,并且通过标准中的常规用途,名称还可以包含预选符,以说出应该查找哪个名称空间或类名称。值依赖于值的表达式
1 + n
不是名称,但n
是。名称的所有相关构造的子集称为依赖性名称。但是,功能名称在模板的不同实例中可能具有不同的含义,但不幸的是,该一般规则并未捕获。依赖函数名称
不是本文的关注点,而是值得一提的:函数名称是单独处理的例外。标识符函数名称本身不是依赖性,而是呼叫中使用的类型依赖参数表达式。在示例中
f((t)0)
,f
是一个因名称。在标准中,这是在(14.6.2/1)
中指定的。注释和示例
在足够的情况下,我们需要
typename
和模板
的其他 。您的代码应该看起来像以下键
Template
不一定总是出现在名称的最后一部分中。它可以在中间出现在用作范围的类名称之前,例如在以下示例中,禁止关键字是禁止的,如
typename
。假定给出的名称是类类型名称。这对于基类列表和构造函数初始器列表中的两个名称都是如此:在使用declarations中, 这是正确的。 ,和C ++委员会在解决方案上。
(See here also for my C++11 answer)
In order to parse a C++ program, the compiler needs to know whether certain names are types or not. The following example demonstrates that:
How should this be parsed? For many languages a compiler doesn't need to know the meaning of a name in order to parse and basically know what action a line of code does. In C++, the above however can yield vastly different interpretations depending on what
t
means. If it's a type, then it will be a declaration of a pointerf
. However if it's not a type, it will be a multiplication. So the C++ Standard says at paragraph (3/7):How will the compiler find out what a name
t::x
refers to, ift
refers to a template type parameter?x
could be a static int data member that could be multiplied or could equally well be a nested class or typedef that could yield to a declaration. If a name has this property - that it can't be looked up until the actual template arguments are known - then it's called a dependent name (it "depends" on the template parameters).You might recommend to just wait till the user instantiates the template:
This will work and actually is allowed by the Standard as a possible implementation approach. These compilers basically copy the template's text into an internal buffer, and only when an instantiation is needed, they parse the template and possibly detect errors in the definition. But instead of bothering the template's users (poor colleagues!) with errors made by a template's author, other implementations choose to check templates early on and give errors in the definition as soon as possible, before an instantiation even takes place.
So there has to be a way to tell the compiler that certain names are types and that certain names aren't.
The "typename" keyword
The answer is: We decide how the compiler should parse this. If
t::x
is a dependent name, then we need to prefix it bytypename
to tell the compiler to parse it in a certain way. The Standard says at (14.6/2):There are many names for which
typename
is not necessary, because the compiler can, with the applicable name lookup in the template definition, figure out how to parse a construct itself - for example withT *f;
, whenT
is a type template parameter. But fort::x * f;
to be a declaration, it must be written astypename t::x *f;
. If you omit the keyword and the name is taken to be a non-type, but when instantiation finds it denotes a type, the usual error messages are emitted by the compiler. Sometimes, the error consequently is given at definition time:The syntax allows
typename
only before qualified names - it is therefor taken as granted that unqualified names are always known to refer to types if they do so.A similar gotcha exists for names that denote templates, as hinted at by the introductory text.
The "template" keyword
Remember the initial quote above and how the Standard requires special handling for templates as well? Let's take the following innocent-looking example:
It might look obvious to a human reader. Not so for the compiler. Imagine the following arbitrary definition of
boost::function
andf
:That's actually a valid expression! It uses the less-than operator to compare
boost::function
against zero (int()
), and then uses the greater-than operator to compare the resultingbool
againstf
. However as you might well know,boost::function
in real life is a template, so the compiler knows (14.2/3):Now we are back to the same problem as with
typename
. What if we can't know yet whether the name is a template when parsing the code? We will need to inserttemplate
immediately before the template name, as specified by14.2/4
. This looks like:Template names can not only occur after a
::
but also after a->
or.
in a class member access. You need to insert the keyword there too:Dependencies
For the people that have thick Standardese books on their shelf and that want to know what exactly I was talking about, I'll talk a bit about how this is specified in the Standard.
In template declarations some constructs have different meanings depending on what template arguments you use to instantiate the template: Expressions may have different types or values, variables may have different types or function calls might end up calling different functions. Such constructs are generally said to depend on template parameters.
The Standard defines precisely the rules by whether a construct is dependent or not. It separates them into logically different groups: One catches types, another catches expressions. Expressions may depend by their value and/or their type. So we have, with typical examples appended:
T
)N
)(T)0
)Most of the rules are intuitive and are built up recursively: For example, a type constructed as
T[N]
is a dependent type ifN
is a value-dependent expression orT
is a dependent type. The details of this can be read in section(14.6.2/1
) for dependent types,(14.6.2.2)
for type-dependent expressions and(14.6.2.3)
for value-dependent expressions.Dependent names
The Standard is a bit unclear about what exactly is a dependent name. On a simple read (you know, the principle of least surprise), all it defines as a dependent name is the special case for function names below. But since clearly
T::x
also needs to be looked up in the instantiation context, it also needs to be a dependent name (fortunately, as of mid C++14 the committee has started to look into how to fix this confusing definition).To avoid this problem, I have resorted to a simple interpretation of the Standard text. Of all the constructs that denote dependent types or expressions, a subset of them represent names. Those names are therefore "dependent names". A name can take different forms - the Standard says:
An identifier is just a plain sequence of characters / digits, while the next two are the
operator +
andoperator type
form. The last form istemplate-name <argument list>
. All these are names, and by conventional use in the Standard, a name can also include qualifiers that say what namespace or class a name should be looked up in.A value dependent expression
1 + N
is not a name, butN
is. The subset of all dependent constructs that are names is called dependent name. Function names, however, may have different meaning in different instantiations of a template, but unfortunately are not caught by this general rule.Dependent function names
Not primarily a concern of this article, but still worth mentioning: Function names are an exception that are handled separately. An identifier function name is dependent not by itself, but by the type dependent argument expressions used in a call. In the example
f((T)0)
,f
is a dependent name. In the Standard, this is specified at(14.6.2/1)
.Additional notes and examples
In enough cases we need both of
typename
andtemplate
. Your code should look like the followingThe keyword
template
doesn't always have to appear in the last part of a name. It can appear in the middle before a class name that's used as a scope, like in the following exampleIn some cases, the keywords are forbidden, as detailed below
On the name of a dependent base class you are not allowed to write
typename
. It's assumed that the name given is a class type name. This is true for both names in the base-class list and the constructor initializer list:In using-declarations it's not possible to use
template
after the last::
, and the C++ committee said not to work on a solution.C ++ 11
问题
当C ++ 03中的规则关于您何时需要
typename
和Template
在很大程度上是合理的,它的公式存在一个令人讨厌的劣势。 ,即使编译器可以完美地确定
a :: result_type
只能是int
(因此,因此是类型),并且> this-&gt; g
只能是成员模板g
以后声明(即使a
在某个地方明确专业化,也不会影响该模板中的代码,因此它的含义不能受到以后的a
的专业化的影响!)。当前的实例化
以改善情况,在C ++ 11中,当类型引用封闭模板时,语言跟踪。要知道,该类型必须是通过使用某种形式的名称来形成的,该名称是其自己的名称(在上面,
a
,a&lt; t&gt;
,<代码> :: a&lt; t&gt; )。该名称引用的一种类型已知是当前的实例化。如果形成名称的类型是成员/嵌套类(然后,,a :: nestedClass
and a 是两个当前的实例化)。基于此概念,该语言说
CurrentAstantiation :: Foo
,foo
和currentInstantiationTypy-&gt; foo
(例如a * a = this; a-&gt; foo
)都是当前实例的成员或它的非依赖性基类之一(通过立即进行名称查找)。关键字
typename
和模板
现在不再需要预选赛,而预选赛是当前实例的成员。要记住的关键点是,a&lt; t&gt;
is stall 一个依赖类型的名称(毕竟t
也是键入的)。但是a&lt; t&gt; :: result_type
是一种类型 - 编译器将“神奇地”查看这种依赖类型以弄清楚这一点。这是令人印象深刻的,但是我们可以做得更好吗?该语言甚至更进一步,并且需要 再次实现
d :: result_type
实例化d :: f :: f
(即使它找到了它的在定义时已经意思)。现在查找结果有所不同或结果歧义,该程序的形式不佳,必须给出诊断。想象一下,如果我们定义c
这样会发生什么,则需要编译器在实例化
d&lt; int&gt; :: f
时捕获错误。因此,您可以获得两个世界中最好的:如果您在依赖基础类方面遇到麻烦,则“延迟”查找,以及“立即”查找,使您从typename
和模板中释放您。
。在
d
的代码中未知的专业,名称
typename d :: Questable_type
不是当前实例化的成员。相反,语言将其标记为未知专业化的成员。特别是,当您执行依赖性tyentTypename :: foo
或dependenttypedname-&gt; foo
时,依赖类型不是 当前的实例化(在这种情况下,编译器可以放弃,并说“稍后我们将查看foo
是什么),或者IT 是 当前的实例化且名称在IT或其非依赖性基类,也有依赖性基类
。 +03,该语言允许捕获此错误,因为永远无法有一种有效的方法来实例化
a&lt; t&gt; :: h
(无论您给出> t
)。在C ++ 11中,该语言现在有进一步的检查,以提供更多编译器实现此规则的原因。成员Questable_type
,名称a&lt; t&gt; :: Questable_type
nese nese nes 都不是当前实例化的成员 and> a未知专业的成员。在这种情况下,该代码在实例化时不应有效编译,因此该语言禁止名称,其中资格符是当前的实例化既不是未知专业化的成员,也不是当前实例化的成员(但是,仍然不需要诊断出这种违规行为)。示例和琐事
您可以在
C ++ 11规则使以下有效的C ++ 03代码不正确(C ++委员会未打算,但可能无法修复)
此有效的C ++ 03代码将绑定
this&gt ; f
toa :: f
在实例化时,一切都很好。 C ++ 11但是,将其绑定到b :: f
,并在实例化时需要进行双重检查,检查查找是否仍然匹配。但是,当实例化c&lt; a&gt; :: g
时,优势规则应用和查找将找到a :: f
而不是。C++11
Problem
While the rules in C++03 about when you need
typename
andtemplate
are largely reasonable, there is one annoying disadvantage of its formulationAs can be seen, we need the disambiguation keyword even if the compiler could perfectly figure out itself that
A::result_type
can only beint
(and is hence a type), andthis->g
can only be the member templateg
declared later (even ifA
is explicitly specialized somewhere, that would not affect the code within that template, so its meaning cannot be affected by a later specialization ofA
!).Current instantiation
To improve the situation, in C++11 the language tracks when a type refers to the enclosing template. To know that, the type must have been formed by using a certain form of name, which is its own name (in the above,
A
,A<T>
,::A<T>
). A type referenced by such a name is known to be the current instantiation. There may be multiple types that are all the current instantiation if the type from which the name is formed is a member/nested class (then,A::NestedClass
andA
are both current instantiations).Based on this notion, the language says that
CurrentInstantiation::Foo
,Foo
andCurrentInstantiationTyped->Foo
(such asA *a = this; a->Foo
) are all member of the current instantiation if they are found to be members of a class that is the current instantiation or one of its non-dependent base classes (by just doing the name lookup immediately).The keywords
typename
andtemplate
are now not required anymore if the qualifier is a member of the current instantiation. A keypoint here to remember is thatA<T>
is still a type-dependent name (after allT
is also type dependent). ButA<T>::result_type
is known to be a type - the compiler will "magically" look into this kind of dependent types to figure this out.That's impressive, but can we do better? The language even goes further and requires that an implementation again looks up
D::result_type
when instantiatingD::f
(even if it found its meaning already at definition time). When now the lookup result differs or results in ambiguity, the program is ill-formed and a diagnostic must be given. Imagine what happens if we definedC
like thisA compiler is required to catch the error when instantiating
D<int>::f
. So you get the best of the two worlds: "Delayed" lookup protecting you if you could get in trouble with dependent base classes, and also "Immediate" lookup that frees you fromtypename
andtemplate
.Unknown specializations
In the code of
D
, the nametypename D::questionable_type
is not a member of the current instantiation. Instead the language marks it as a member of an unknown specialization. In particular, this is always the case when you are doingDependentTypeName::Foo
orDependentTypedName->Foo
and either the dependent type is not the current instantiation (in which case the compiler can give up and say "we will look later whatFoo
is) or it is the current instantiation and the name was not found in it or its non-dependent base classes and there are also dependent base classes.Imagine what happens if we had a member function
h
within the above definedA
class templateIn C++03, the language allowed to catch this error because there could never be a valid way to instantiate
A<T>::h
(whatever argument you give toT
). In C++11, the language now has a further check to give more reason for compilers to implement this rule. SinceA
has no dependent base classes, andA
declares no memberquestionable_type
, the nameA<T>::questionable_type
is neither a member of the current instantiation nor a member of an unknown specialization. In that case, there should be no way that that code could validly compile at instantiation time, so the language forbids a name where the qualifier is the current instantiation to be neither a member of an unknown specialization nor a member of the current instantiation (however, this violation is still not required to be diagnosed).Examples and trivia
You can try this knowledge on this answer and see whether the above definitions make sense for you on a real-world example (they are repeated slightly less detailed in that answer).
The C++11 rules make the following valid C++03 code ill-formed (which was not intended by the C++ committee, but will probably not be fixed)
This valid C++03 code would bind
this->f
toA::f
at instantiation time and everything is fine. C++11 however immediately binds it toB::f
and requires a double-check when instantiating, checking whether the lookup still matches. However when instantiatingC<A>::g
, the Dominance Rule applies and lookup will findA::f
instead.。
基本目的是相同的;对“何时?”的解释和“为什么?”
必须应用
和 template 。typename
和模板
的目的是什么?typename
和模板
在声明模板以外的其他情况下是可用的。在 c ++ 中有某些上下文,必须明确地告诉编译器如何处理名称,并且所有这些上下文都有一个共同点;它们取决于至少一个模板参数。
我们指的是解释中可能存在歧义的名称,为; “ 相关名称”。
这篇文章将为依赖名称和两个关键字之间的关系提供解释。
摘要说超过1000个单词
试图解释以下 function-template 对自己,朋友或您的猫的情况;标记( a )的语句中发生了什么?
它可能并不像人们想象的那样容易,更具体地说是评估( a )的结果,重大 作为模板参数的定义 。
不同的
t
可以大大更改所涉及的语义。如果我们用类型 x 实例化函数 - 板,则两个不同的方案:
,如( c ),我们将声明 pointer-to int naty x ,但是;
如果我们使用类型 y 实例 123 的乘积乘以一些已经声明的变量 x 。
C ++标准的理由
至少在这种情况下关心我们的安全和福祉。
为了防止实施潜在的令人讨厌的惊喜,我们通过 明确地来解决A 依赖名称的歧义的标准任务将名称视为A type-name 或A template-id 。
如果没有说明,则依赖性名称将被视为变量或函数。
如何处理依赖名称?
如果这是好莱坞电影,依赖性名称将是通过身体接触传播的疾病,会立即影响其宿主,使其混淆。混乱可能会导致一个形成不良的人,Erhm ..程序。
依赖性名称是直接或间接取决于A 模板参数的任何名称。
我们在上面的片段中有四个依赖性名称:
的实例化,
womentrait&lt; t&gt;
,其中包括t
和;witherrait&lt; t&gt;
and;withrait&lt; t&gt; ,;
的实例化,
woryrait&lt; t&gt;
。如果编译器会解释依赖性名称<,则语句( e ),( f )或( g )是有效的/em>作为变量/函数(如前所述,如果我们不明确说另有说明,就会发生这种情况)。
使
g_tmpl
具有有效定义的解决方案,我们必须明确地告诉编译器,我们期望在( e ),( f )中的A template-id 和a type ,以及 template-id in(< em> g )。
每次a name 表示类型, all name 必须是 type-names 或 namespaces ,考虑到这一点,很容易看到我们在完全合格的名称开始时应用
typename
。模板
在这方面是不同的,因为没有办法得出结论,例如; “哦,这是一个模板,然后另一件事也必须是模板” 。这意味着我们直接在我们想这样对待的任何名称的前面应用模板>。
我可以将关键字贴在任何名称前面吗?
只要您要处理 commiquied -name -name ( k k <),标准中的规则可以应用关键字/em>),但是如果名称不是合格的,则应用程序不正确( l )。
注意:应用
typename
或模板
在不需要的上下文中被视为好练习;仅仅因为您可以做某事,并不意味着您应该做。此外,在某些情况下,
typename
和template
是明确禁止:指定类继承的基础
在派生类的 base-specifier-list 中编写的每个名称已经被视为 type-name ,明确指定
typename
均为形成不良和多余的。当 template-id 是使用指导性
中引用的
The underlying purpose is the same; an explanation to "When?" and "Why?"
typename
andtemplate
must be applied.What is the purpose of
typename
andtemplate
?typename
andtemplate
are usable in circumstances other than when declaring a template.There are certain contexts in C++ where the compiler must explicitly be told how to treat a name, and all these contexts have one thing in common; they depend on at least one template-parameter.
We refer to such names, where there can be an ambiguity in interpretation, as; "dependent names".
This post will offer an explanation to the relationship between dependent-names, and the two keywords.
A snippet says more than 1000 words
Try to explain what is going on in the following function-template, either to yourself, a friend, or perhaps your cat; what is happening in the statement marked (A)?
It might not be as easy as one thinks, more specifically the result of evaluating (A) heavily depends on the definition of the type passed as template-parameter
T
.Different
T
s can drastically change the semantics involved.The two different scenarios:
If we instantiate the function-template with type X, as in (C), we will have a declaration of a pointer-to int named x, but;
if we instantiate the template with type Y, as in (D), (A) would instead consist of an expression that calculates the product of 123 multiplied with some already declared variable x.
The Rationale
The C++ Standard cares about our safety and well-being, at least in this case.
To prevent an implementation from potentially suffering from nasty surprises, the Standard mandates that we sort out the ambiguity of a dependent-name by explicitly stating the intent anywhere we'd like to treat the name as either a type-name, or a template-id.
If nothing is stated, the dependent-name will be considered to be either a variable, or a function.
How to handle dependent names?
If this was a Hollywood film, dependent-names would be the disease that spreads through body contact, instantly affects its host to make it confused. Confusion that could, possibly, lead to an ill-formed perso-, erhm.. program.
A dependent-name is any name that directly, or indirectly, depends on a template-parameter.
We have four dependent names in the above snippet:
SomeTrait<T>
, which includeT
, and;SomeTrait<T>
, and;SomeTrait<T>
, and;SomeTrait<T>
.Neither of statement (E), (F) or (G) is valid if the compiler would interpret the dependent-names as variables/functions (which as stated earlier is what happens if we don't explicitly say otherwise).
The solution
To make
g_tmpl
have a valid definition we must explicitly tell the compiler that we expect a type in (E), a template-id and a type in (F), and a template-id in (G).Every time a name denotes a type, all names involved must be either type-names or namespaces, with this in mind it's quite easy to see that we apply
typename
at the beginning of our fully qualified name.template
however, is different in this regard, since there's no way of coming to a conclusion such as; "oh, this is a template, then this other thing must also be a template". This means that we applytemplate
directly in front of any name that we'd like to treat as such.Can I just stick the keywords in front of any name?
The rules in the Standard states that you may apply the keywords as long as you are dealing with a qualified-name (K), but if the name isn't qualified the application is ill-formed (L).
Note: Applying
typename
ortemplate
in a context where it is not required is not considered good practice; just because you can do something, doesn't mean that you should.Additionally there are contexts where
typename
andtemplate
are explicitly disallowed:When specifying the bases of which a class inherits
Every name written in a derived class's base-specifier-list is already treated as a type-name, explicitly specifying
typename
is both ill-formed, and redundant.When the template-id is the one being referred to in a derived class's using-directive
这个答案是一个相当简短而甜蜜的答案,可以回答标题为“问题的一部分”。如果您想提供更多详细的答案,以解释为什么必须将它们放在那里,请访问在这里。
放置
typeName
关键字的一般规则主要是在使用模板参数时,并且要访问嵌套typedef
或使用-Alias,for for示例:请注意,这也适用于元功能或采用通用模板参数的事物。但是,如果提供的模板参数为显式类型,则不必指定
typename
,例如:添加
template
vilifier的常规规则大多相似它们通常涉及本身被模板的结构/类的模板成员函数(静态或其他),例如:给定此结构和功能:
尝试从内部访问
t.get&lt; int&gt;()
该函数将导致错误:,在此上下文中,您需要事先调用
template
关键字,然后将其称为:t.template get&lt; int&gt;()
因此 编译器将正确解析此而不是
t.get&lt; int
。This answer is meant to be a rather short and sweet one to answer (part of) the titled question. If you want an answer with more detail that explains why you have to put them there, please go here.
The general rule for putting the
typename
keyword is mostly when you're using a template parameter and you want to access a nestedtypedef
or using-alias, for example:Note that this also applies for meta functions or things that take generic template parameters too. However, if the template parameter provided is an explicit type then you don't have to specify
typename
, for example:The general rules for adding the
template
qualifier are mostly similar except they typically involve templated member functions (static or otherwise) of a struct/class that is itself templated, for example:Given this struct and function:
Attempting to access
t.get<int>()
from inside the function will result in an error:Thus in this context you would need the
template
keyword beforehand and call it like so:t.template get<int>()
That way the compiler will parse this properly rather than
t.get < int
.但是,我不确定您的实现是正确的。如果我正确理解,则不应该实例化此类,因此“失败”选项卡将永远不会失败。也许更好地指示该类型是否具有简单的布尔值。
PS:看看
” :看看 Typelists ,特别是在Andrei Alexandrescu的书中:
However, I'm not sure you're implementation of inUnion is correct. If I understand correctly, this class is not supposed to be instantiated, therefore the "fail" tab will never avtually fails. Maybe it would be better to indicates whether the type is in the union or not with a simple boolean value.
PS: Have a look at Boost::Variant
PS2: Have a look at typelists, notably in Andrei Alexandrescu's book: Modern C++ Design
c ++ 20 aka c ++ 2a
如此提案,C ++ 20/C ++ 2A进一步放松了
typename
关键字的要求。特别是,typeName
现在可以在所有这些位置省略,在句法上只有类型是合法的。因此,如果未知令牌必须是一种类型,则C ++ 20实际上将其视为一种类型。对于向后兼容,typename
仍可以使用。特别是,使用和
typedef
声明的大多数现在可以编写
,typename
。typename
也可以在方法返回类型的声明(包括尾声返回类型),方法和lambda参数的声明以及类型参数中省略static_cast
,<代码> const_castdynamic_cast
和reinterpret_cast
。一个值得注意的例外是
typeName
仍然需要的是在用户或库的实例化的参数列表中:即使,如果该特定参数被声明为一种类型,则typeName < /代码>仍然需要关键字。 SO
是错误的,如果a是依赖范围,static_cast&lt; a :: b&gt;(arg)
在C ++ 20中是合法的,但是my_template_class&lt; a :: b&gt;(arg gt)
my_template_class
期望类型。一些例子:
C++20 aka C++2a
As outlined in this Proposal, C++20 / C++2a has further relaxed the requirements for the
typename
keyword. In particular,typename
may now be omitted in all those places, where syntactically only a type is legal. So, if an unknown token must be a type, C++20 will actually treat it as a type. For backwards compatibility,typename
may still be used, though.In particular, most
using
andtypedef
declarations can now be written withouttypename
.typename
can also be omitted in the declaration of method return types (including trailing return types), in the declaration of method and lambda parameters and in the type argument tostatic_cast
,const_cast
,dynamic_cast
andreinterpret_cast
.One notable exception, where
typename
is still required, is in the argument list of instantiations of user or library defined templates: Even, if that particular argument was declared to be a type, thetypename
keyword is still required. Sostatic_cast<A::B>(arg)
is legal in C++20, butmy_template_class<A::B>(arg)
is ill-formed, if A is a dependant scope andmy_template_class
expects a type.A few examples: