c++ - 在源文件中声明函数模板时是否应该防止 ODR 违规?
问题描述
一个“普通”函数,当在单个翻译单元中单独定义和使用时,声明和定义如下:
// implementation.cpp
static void fun(int arg) { /* implementation */ }
或者,您可以删除static
关键字并将函数包装在未命名的命名空间中。但未能执行上述任一操作可能会导致ODR 违规:如果不同的翻译单元也具有声明/定义fun
(具有相同的参数),则喜欢者可能不得不默默地丢弃其中一个实现以保留单个定义(在在这种情况下,你最好对两者都有完全相同的定义,否则你会看到意想不到的行为)。
如上所述,问题更“面向实现”,但即使是标准也会敦促您使用static
or来防止此类副作用namespace { /**/ }
。
问题是什么时候fun
是函数模板?我是否还需要将其包装在未命名的命名空间中或在翻译单元内将其声明为静态?
template <class T>
/* static ? */ void fun(T arg) { /*...*/ }
在cppreference中提到,如果函数inline
显然没问题(对单一定义的要求仅针对非内联函数提出),因此无需担心 ODR:
需要在整个程序中出现每个 odr 使用的非内联函数或变量(见下文)的一个且只有一个定义
我们知道函数模板是内联的,但这足以保证 ODR 的正确性吗?
解决方案
在cppreference中提到,如果标记了函数(带有外部链接inline
),则必须格外小心,因为该定义必须存在于使用该函数的每个翻译单元中。当有多个定义时,必须满足几个条件,包括:
每个定义都由相同的标记序列组成
如果不满足此要求,则程序格式错误,不需要诊断。此要求同样适用于内联函数和函数模板。有时这会正常工作,但如果编译器选择不内联对您的函数的调用(inline
关键字不会影响这一点),您最终可能会调用错误版本的函数。不需要链接器来检测这一点。
事实上,这是链接器倾向于默默忽略的 ODR 违规。尽管问题中声称,当有多个定义具有外部链接的非内联函数时,链接器往往会产生错误并中止。
注意:标记函数static
或将其包含在匿名命名空间中将导致它不再具有外部链接。
我想指出,这个问题以一种相当常见但非标准的方式使用了“显然”这个词,意思是“我需要以下虚假陈述为真,所以毫无疑问地接受它。” 每当你觉得有必要证明某事是“显而易见的”时,学会停下来质疑自己是很有用的。
推荐阅读
- r - R Markdown - 在 R 外部使用 shyni 运行时打开 .Rmd
- objective-c - Xcode & Objective-C:takebox SDK 是否能够在视频入门电话和 iOS 设备之间进行 SIP 视频通话?
- reactjs - 如何在浏览器中添加此分数并显示总分?链接:https://api.myjson.com/bins/1dlper
- php - Select2 在模态 Bootstrap 中工作。但我有一个带有其他 id 的模态引导程序
- node.js - 与 mongo shell 或 robo3t 性能相比,NodeJs mongoose 性能较慢
- laravel - 如何在 Laravel 中创建自定义错误 419:页面已过期?
- laravel - 响应工厂 Laravel 上不存在 Cookie
- c# - 如何在同一个项目中单独运行 .cs 文件并出现在同一个文件夹中
- java - 在表格 XWPF 文档后添加分页符
- javascript - 如何获取数组中最频繁/出现的元素