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