为了账号安全,请及时绑定邮箱和手机立即绑定

我必须在何处以及为何要使用“模板”和“typename”关键字?

我必须在何处以及为何要使用“模板”和“typename”关键字?

有只小跳蛙 2019-05-20 16:07:40
在模板,在那里,为什么我必须把typename和template上依赖的名字呢?究竟什么是依赖名称?我有以下代码: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 {         char fail[ -2 + (sizeof(U)%2) ]; // Cannot be instantiated for any U     };     template< > struct inUnion<T> {     };};我遇到的问题就在typedef Tail::inUnion<U> dummy于此。我很确定这inUnion是一个从属名称,VC ++在窒息时非常正确。我也知道我应该能够添加template某个地方告诉编译器inUnion是一个模板ID。但到底在哪里?然后它应该假设inUnion是一个类模板,即inUnion<U>命名一个类型而不是一个函数?
查看完整描述

4 回答

?
手掌心

TA贡献1942条经验 获得超3个赞

是什么目的typenametemplate

typename并且template可以在声明模板以外的情况下使用。

C ++中有某些上下文,其中必须明确地告诉编译器如何处理名称,并且所有这些上下文都有一个共同点; 它们依赖于至少一个模板参数

我们指的是这样的名称,在解释中可能存在歧义,因为; “ 从属名称 ”。

这篇文章将解释依赖名称和两个关键字之间的关系。


一个SNACKET超过1000字

尝试解释以下功能模板中发生的事情,无论是对自己,朋友,还是你的猫; 标记为(A)的声明中发生了什么?

template<class T> void f_tmpl () { T::foo * x; /* <-- (A) */ }


它可能不像人们想象的那么容易,更具体地说,评估(A)的结果在很大程度上取决于作为模板参数传递的类型的定义T

不同的Ts可以彻底改变所涉及的语义。

struct X { typedef int       foo;       }; /* (C) --> */ f_tmpl<X> ();struct Y { static  int const foo = 123; }; /* (D) --> */
 f_tmpl<Y> ();


两种不同的场景

  • 如果我们用类型X实例化函数模板,就像在(C)中一样,我们将声明一个名为x指向int指针,但是;

  • 如果我们用类型Y实例化模板,如(D)中所示,(A)将由一个表达式组成,该表达式计算123的乘积乘以一些已经声明的变量x



理由

C ++标准关心我们的安全和幸福,至少在这种情况下。

为了防止实现可能遭受令人讨厌的意外,标准要求我们通过明确说明我们想要将名称视为类型名称模板的任何地方来明确依赖名称的歧义。id

如果没有说明,则依赖名称将被视为变量或函数。



如何处理依赖名称

如果这是一部好莱坞电影,依赖名字将是通过身体接触传播的疾病,立即影响其主人,使其混淆。混乱可能会导致一个形成不良的人,erhm ..计划。

从属名称任何名称直接或间接依赖于模板的参数。 

template<class T> void g_tmpl () {
   SomeTrait<T>::type                   foo; // (E), ill-formed
   SomeTrait<T>::NestedTrait<int>::type bar; // (F), ill-formed
   foo.data<int> ();                         // (G), ill-formed    }

我们在上面的代码段中有四个依赖名称:

  • E

    • “类型”取决于实例化SomeTrait<T>,包括T和;

  • F

    • “NestedTrait”,它是一个模板ID,取决于SomeTrait<T>,和;

    • F)末尾的“type”取决于NestedTrait,它取决于SomeTrait<T>,和;

  • G

    • “data”看起来像一个成员函数模板,间接是一个依赖名称,因为foo的类型取决于它的实例化SomeTrait<T>

如果编译器将依赖名称解释为变量/函数(如前所述,如果我们没有明确说明,则会发生的情况),语句(E),(F)或(G)都不是有效的。 

解决方案

为了使g_tmpl有一个有效的定义,我们必须明确告诉编译器我们期望(E)中的类型,(F)中的模板ID类型,以及(G)中的模板ID

template<class T> void g_tmpl () {
   typename SomeTrait<T>::type foo;                            // (G), legal
   typename SomeTrait<T>::template NestedTrait<int>::type bar; // (H), legal
   foo.template data<int> ();                                  // (I), legal}

每次名称表示一个类型时, 涉及的所有名称都必须是类型名称名称空间,考虑到这一点,我们很容易看到我们typename在完全限定名称的开头应用。

template但是,在这方面是不同的,因为没有办法得出如下结论; “哦,这是一个模板,那么另外一件事也必须是模板”。这意味着我们template直接在我们想要处理的任何名称前面申请。



我是否只能在任何名字的前面贴上关键词

“ 我可以坚持typenametemplate在任何名字前面吗?我不想担心它们出现的背景...... ” -Some C++ Developer

标准中的规则规定,只要您处理限定名称K),就可以应用关键字,但如果名称不合格,则应用程序格式错误(L)。

namespace N {
  template<class T>
  struct X { };}

         N::         X<int> a; // ...  legaltypename N::template X<int> b; // (K), legaltypename template    
         X<int> c; // (L), ill-formed

注意:申请typenametemplate在不需要的情况下,不被视为良好做法; 仅仅因为你可以做某事,并不意味着你应该做。


此外,还有地方环境typenametemplate明确禁止:

  • 指定类继承的基础时

    在派生类的base-specifier-list中编写的每个名称都已被视为类型名称,显式指定typename既是格式错误又是冗余。

                       // .------- the base-specifier-list
     template<class T> // v
     struct Derived      : typename SomeTrait<T>::type /* <- ill-formed */ {
       ...
     };


  • template-id是派生类的using-directive中引用的那个时

     struct Base {
       template<class T>
       struct type { };
     };
    
     struct Derived : Base {
       using Base::template type; // ill-formed
       using Base::type;          // legal
     };


查看完整回答
反对 回复 2019-05-20
  • 4 回答
  • 0 关注
  • 768 浏览

添加回答

举报

0/150
提交
取消
意见反馈 帮助中心 APP下载
官方微信