• c++11-17 模板核心知识(十四)—— 解析模板之依赖型模板名称 Dependent Names of Templates(.template/->template/::template)


    有时间的建议先看下上篇文章 : c++11-17 模板核心知识(十三)—— 名称查找与ADL

    tokenization与parsing

    绝大多数语言在编译的时候都有两个阶段:

    • tokenization,或者叫scanning/lexing
    • parsing

    tokenization阶段会读取源码并生成一系列token. 例如:int *p = 0;,tokenizer会生成关键字int、运算符*、标识符p、运算符=、整数0、运算符;

    接下来,parser会递归的减少标记,寻找已知的模式。例如:token 0是一个合法的表达式,*p组合也是一个合法的声明,它和后面的=0组合也是一个合法初始化声明。最后,int是一个已知的类型,后面跟着初始化声明 : *p=0,所以,我们得到了一个初始化p的声明

    解析模板之类型的依赖名称 Dependent Names of Templates

    关于模板解析有六个大方面:

    • 非模板中的上下文相关性 Context Sensitivity in Nontemplates
    • 依赖型类型名称 Dependent Names of Types
    • 依赖型模板名称 Dependent Names of Templates <-----
    • using-declaration中的依赖型名称 Dependent Names in Using Declarations
    • ADL和显式模板实参 ADL and Explicit Template Arguments
    • 依赖性表达式 Dependent Expressions

    这篇文章先讲下代码中比较常见的第三点 : 依赖型模板名称(Dependent Names of Templates)

    这里有一个很重要的概念 :c++11-17 模板核心知识(十三)—— 名称查找与ADL中介绍过的Dependent Name:依赖于模板参数的名称,也就是访问运算符左面的表达式类型依赖于模板参数。例如:std::vector::iterator是一个 Dependent Name,但假如T是一个已知类型的别名(using T = int),那就不是Dependent Name。

    通常而言, 编译器会把模板名称后面的<当做模板参数列表的开始,否则,<就是比较运算符。但是,当引用的模板名称是Dependent Name时,编译器不会假定它是一个模板名称,除非显示的使用template关键字来指明,模板代码中常见的->template.template::template就应用于这种场景中。

    下面看几个例子。

    Example One

    template<unsigned long N>
    void printBitset (std::bitset<N> const& bs) {
        std::cout << bs.template to_string<char, std::char_traits<char>, std::allocator<char>>();
    }
    

    这里,参数bs依赖于模板参数N。所以,我们必须通过template关键字让编译器知道bs是一个模板名称,否则按照上面的规则,<会被当做比较符——小于号。

    Example Two

    The template keyword as qualifier (C++ only)中的例子:

    #include <iostream>
    using namespace std;
    
    class X {
       public:
          template <int j> struct S {
             void h() {
                cout << "member template's member function: " << j << endl;
             }
          };
          template <int i> void f() {
            cout << "Primary: " << i << endl;
          }
    };
    
    template<> void X::f<20>() {
       cout << "Specialized, non-type argument = 20" << endl;
    }
    
    template<class T> void g(T* p) {
       p->template f<100>();
       p->template f<20>();
       typename T::template S<40> s; // use of scope operator on a member template
       s.h();
    }
    
    int main()
    {
       X temp;
       g(&temp);
    }
    

    这里,参数p依赖模板参数T。注意typename T::template S<40> s;的使用。

    Example Three

    template <typename T> class Shell {
    public:
      template <int N> class In {
      public:
        template <int M> class Deep {
        public:
          virtual void f();
        };
      };
    };
    
    template <typename T, int N> class Weird {
    public:
      void case1(typename Shell<T>::template In<N>::template Deep<N> *p) {
        p->template Deep<N>::f();      // inhibit virtual call
      }
    
      void case2(typename Shell<T>::template In<N>::template Deep<N> &p) {
        p.template Deep<N>::f();      // inhibit virtual call
      }
    };
    

    参数p依赖模板参数T。编译器不会去判断p.Deep是不是模板。如果不指定template,那么p.Deep<N>::f()就会被解析成((p.Deep)<N)>f();<被当做比较符。

    基于上面的例子,我们也可以知道,->template.template::template只存在于模板中,并且是在Dependent Name的场景下使用(依赖于模板参数)。

    (完)

    朋友们可以关注下我的公众号,获得最及时的更新:

    image

  • 相关阅读:
    python3 urllib 类
    python 金角大王博客园学习地址
    配置文件一mapper.xml
    配置文件一mybatis-config.xml
    配置文件一applicationContext.xml
    类文件路径一classpath
    配置文件一web.xml
    日志框架一logback配置和使用
    SpringBoot + redis实现分布式session共享
    SpringBoot集成Redis 一 分布式锁 与 缓存
  • 原文地址:https://www.cnblogs.com/zhangyachen/p/14102445.html
Copyright © 2020-2023  润新知