• 作者:老汪软件技巧
  • 发表时间:2024-10-16 21:02
  • 浏览量:

1. 摘要:

".template"需要使用“”指定模版参数时需要加,不需要使用“”指定模版参数编译器可以自动推导时可以不加

2. 背景:2.1. 存疑代码:

if (item.first.template as() == kDefaultExptId) {
  return;
}

这里的template关键字是什么作用?是不是必须的呢?

2. 参考资料:

1. cppreference

Dependent names -

“.template”在这里被称作template disambiguator,参考 The template disambiguator for dependent names 一节,作用是帮助编译器区分名称,避免二义性。

2. 标准文档

C++标准文档中14.2节中的说明

3. 分析

二义性:一个语句或符号有两种及以上的解释和含义,就是具有二义性,意味着含义不清楚不明确。必须消除二义性程序才能正常运行

对于''符号来说有多种含义,组合成""表示模版,组合成""表示位移,单独的符号表示小于和大于

同一个符号中不同的场景下表示不同的含义,但是这些场景会有重合,此时就产生了二义性。

例如

 node.as(0);

有两种含义

(1)调用了 node 的模版成员函数as,模版参数类型为T,传入了参数0

(2)比较 node 的成员变量 as 是否小于 T,再比较前面的结果是否大于0

由于一般人的思维惯性,看到就会想到模版,想到是第一种解释,但是按照标准规定,这里的as会被优先解释为非模版名,会被解释为小于和大于,是第二种含义,出现了代码看起来和实际不一样的情况。

为了使得上述代码被解释为第一种含义,增加template关键字即可,如下

 node.template as<T>(0);

有必要没话找话吗_有必要没收孩子的手机吗_

存疑代码加与不加template的对比

优点缺点

加上 template

可以避免不符合预期的代码,是否使用了模版代码更明确,符合标准,可移植性好

多了个单词,略显啰嗦

不加 template

代码简洁

可能出现不符合预期的代码,引入意外的bug,可移植性差

综合考虑:对于依赖模版的项目,为了保证代码被解释的正确性和可移植性,加上是一个更好的选择

4. 测试

#include 
struct Node {
  int as = 1;
};
int main() {
  Node node;
  // 期望是调用模版函数,实际是比较node的成员变量as是否小于1,然后比较前面的结果是否大于100,看起来和调用了模版函数的形式一样
  auto ret = node.as<1>(100);
  // 结果是0
  std::cout << "ret:" << ret << std::endl;
  // 如果加上template,则会编译不通过,并给出非模版的提示
  auto ret_1 = node.template as<1>(100);  // 这行代码编译不通过
  return 0;
}

5. 示例

1. 标准库实现中有大量这种用法

/llvm/llvm-p…

2. 一些优秀的开源项目也在使用,可以搜到很多类似代码

在不会产生歧义的地方,template关键字可加可不加,一些优秀的开源项目也选择了加上,佐证了这是一个好的习惯,特别是面对复杂的模板时,明确的写法让作者和用户都对代码更有信心

6. 总结

业务代码中这种用法并不多见,多数情况下即使被解释为比较操作符该语句也是不合法的,编译器自动将它作为模版处理了。按照规范,为了语法上避免二义性,要求严格的编译器对于没加的代码编译不过;更聪明的编译器可以根据语义判断不会存在二义性,同时为了代码简洁,所以也允许编译通过这样形式的代码。

为了避免代码歧义的bug和良好的可移植性,在调用模板成员函数需要使用指定模版参数的加上template,不需要指定模版参数的可以不加,IDE也有加和不加两个版本的提示,统一即可

7. 思考:

C++中还有其他东西是可加可不加的吗?当然有,类似上文的typename disambiguator,还有 if 条件语句后面的"{}"。有的人习惯都加上,对代码心存敬畏之心,总是以最稳妥最不容易出错的方式编码,心里想着自己的代码可能有一天运行在很多地方;有的人更喜欢简洁,但凡可以不要的东西统统扔掉。规范允许不加,是把选择权交给开发者,简单的不完备的写法,在一些情况下也是需要的,例如单测,一个小脚本,修复紧急问题等。

当然这样非强制性的规范,会让新的开发者踩了这个坑才知道,甚至会重复犯错误,每走一步都担心掉入陷阱,理解了背后的缘由,就像有了地图指引。新的语言没有历史包袱,例如rust,强制规范解决了C++很多坑,但是同时上手难度大,是严格版的C++。

规范之所以不是标准,因为不遵守代码也可以正常运行,是否遵守取决于环境。它存在的意义是减少错误,在多数情况下可以省略是为了方便,如何权衡是代码的美学,既不啰嗦,也不晦涩,游刃有余,自然合理,这需要有大量经验。语言本身也是在进化的,以前必须有的东西后面可能没有更好,进化的语言才有生命力。