首页 > 解决方案 > 具有包含对静态对象的引用的外部链接的内联函数定义

问题描述

有一个约束6.7.4(p3)

具有外部链接的函数的内联定义不应包含具有静态或线程存储持续时间的可修改对象的定义,并且不应包含对具有内部链接的标识符的引用。

考虑以下示例:

static const int i = 10;

void do_print(void);

inline void do_print(void){
    printf("%d/n", i); //Reference to an identifier with internal linkage
                       //constraint violation
}

演示

在这里,具有外部链接的函数的内联定义使用具有内部链接的标识符。所以根据5.1.1.3(p1)

如果预处理翻译单元或翻译单元包含违反任何语法规则或约束的行为,则符合要求的实现应产生至少一个诊断 消息(以实现定义的方式标识),即使该行为也明确指定为未定义或实现-定义。

我预计编译器会以某种方式报告违反此约束的情况(一些警告)。但是代码编译得很好,没有警告或产生其他一些消息。

问题是:为什么在违反上述约束的情况下不产生诊断消息?

标签: clanguage-lawyerinline-functions

解决方案


cppreference有一段解释了其背后的基本原理:

如果一个函数在某些翻译单元中声明为内联,则不需要在任何地方都声明为内联:最多一个翻译单元也可以提供一个常规的、非内联的非静态函数,或者一个声明为 extern inline 的函数。据说这一翻译单元提供了外部定义。如果在表达式中使用具有外部链接的函数名称,则程序中必须存在一个外部定义,请参见一个定义规则。

如果程序中存在外部定义,则函数的地址始终是外部函数的地址,但是当该地址用于进行函数调用时,未指定是内联定义(如果存在于翻译单元中)还是调用外部定义。

一个注释还说(强调我的):

inline 关键字取自 C++,但在 C++ 中,如果函数声明为 inline,则必须在每个翻译单元中声明为 inline,并且每个 inline 函数的定义必须完全相同(在 C 中,定义可能是不同,只要程序的行为不依赖于差异)。另一方面,C++ 允许非常量函数局部静态,并且来自内联函数的不同定义的所有函数局部静态在 C++ 中是相同的,但在 C 中是不同的。

这意味着,如果本地内联函数static const value在一个翻译单元中使用 a,则可以在不同的翻译单元中定义具有相同名称的非内联函数,并且静态 const 变量的值不同,从而导致显式 UB,因为未指定是否编译器将使用全局非内联版本的本地内联。


推荐阅读