首页 > 解决方案 > 在源文件中声明函数模板时是否应该防止 ODR 违规?

问题描述

一个“普通”函数,当在单个翻译单元中单独定义和使用时,声明和定义如下:

// implementation.cpp
static void fun(int arg) { /* implementation */ }

或者,您可以删除static关键字并将函数包装在未命名的命名空间中。但未能执行上述任一操作可能会导致ODR 违规:如果不同的翻译单元也具有声明/定义fun(具有相同的参数),则喜欢者可能不得不默默地丢弃其中一个实现以保留单个定义(在在这种情况下,你最好对两者都有完全相同的定义,否则你会看到意想不到的行为)。

如上所述,问题更“面向实现”,但即使是标准也会敦促您使用staticor来防止此类副作用namespace { /**/ }

问题是什么时候fun函数模板?我是否还需要将其包装在未命名的命名空间中或在翻译单元内将其声明为静态?

template <class T>
/* static ? */ void fun(T arg) { /*...*/ }

cppreference中提到,如果函数inline显然没问题(对单一定义的要求仅针对非内联函数提出),因此无需担心 ODR:

需要在整个程序中出现每个 odr 使用的非内联函数或变量(见下文)的一个且只有一个定义

我们知道函数模板是内联的,但这足以保证 ODR 的正确性吗?

标签: c++templatesone-definition-rule

解决方案


cppreference中提到,如果标记了函数(带有外部链接inline),则必须格外小心,因为该定义必须存在于使用该函数的每个翻译单元中。当有多个定义时,必须满足几个条件,包括:

每个定义都由相同的标记序列组成

如果不满足此要求,则程序格式错误,不需要诊断。此要求同样适用于内联函数和函数模板。有时这会正常工作,但如果编译器选择不内联对您的函数的调用(inline关键字不会影响这一点),您最终可能会调用错误版本的函数。不需要链接器来检测这一点。

事实上,是链接器倾向于默默忽略的 ODR 违规。尽管问题中声称,当有多个定义具有外部链接的非内联函数时,链接器往往会产生错误并中止。

注意:标记函数static或将其包含在匿名命名空间中将导致它不再具有外部链接。


我想指出,这个问题以一种相当常见但非标准的方式使用了“显然”这个词,意思是“我需要以下虚假陈述为真,所以毫无疑问地接受它。” 每当你觉得有必要证明某事是“显而易见的”时,学会停下来质疑自己是很有用的。


推荐阅读