模板静态成员定义取决于传递给链接器的顺序

Template static member definition depends on order passed to linker

下面的代码,其中有2个模板静态字段成员的定义,每个定义定义template1<int>::x具有不同的值。

人们会期望链接器拒绝此类重新定义,因为它们具有不同的值。

但是 g++ 和 MSVC 的编译和链接都通过了,使用哪个定义取决于将源传递给链接器的顺序。

此行为是否符合 C++ 标准、未定义行为或链接器错误?

my_template.h

template <class T>
class template1
{
public:
    static int x;
};

Src2.cpp

#include <stdio.h>
#include "my_template.h"

template <class T>
int template1<T>::x = 2; 

void my_func() // definition
{
    printf("my_func: template1<int>::x = %d\n", template1<int>::x); // definition of X to 2.
    printf("my_func: template1<char>::x = %d\n", template1<char>::x); // definition of X to 2.
}

Main.cpp

#include <cstdio>
#include "my_template.h"

template <class T>
int template1<T>::x = 1;

void my_func();

int main()
{
    printf("main: template1<int>::x = %d\n", template1<int>::x); // definition of X to 1.
    my_func();
    return 0;
}

使用 g++ 编译(MinGW.org GCC Build-20200227-1)9.2.0+

编译1

g++ -o prog Src2.cpp Main.cpp

输出1

main: template1<int>::x = 2
my_func: template1<int>::x = 2
my_func: template1<char>::x = 2

编译2

g++ -o prog Main.cpp Src2.cpp

输出2

main: template1<int>::x = 1
my_func: template1<int>::x = 1
my_func: template1<char>::x = 2

也观察到

Microsoft (R) C/C++ Optimizing Compiler Version 19.25.28612 for x86

当我用-S标志反汇编代码时,每个编译单元都定义了相同的符号名称。

Nightra 合作。

这违反了 ODR(要求实体必须 完全 一个定义,如果使用的话)。所以程序有UB.

编译器无法诊断这个,因为每个翻译单元都很好。理论上,链接器 可以 诊断此问题,但实际上它不会那样做。

Is this behavior compliant to the C++ standard, undefined behavior, or a linker bug?

这是未定义的行为 (UB)。


来自 [basic.def.odr]/4 of N4659 [强调 我的]:

Every program shall contain exactly one definition of every non-inline function or variable that is odr-used in that program outside of a discarded statement; no diagnostic required. The definition can appear explicitly in the program, it can be found in the standard or a user-defined library, or (when appropriate) it is implicitly defined (see [class.ctor], [class.dtor] and [class.copy]). An inline function or variable shall be defined in every translation unit in which it is odr-used outside of a discarded statement.

constexpr static 模板的成员变量是不是隐式inline,因此这是UB,不需要诊断。

我们也可能转向 [basic.def.odr]/6 以获得更强有力的声明(甚至不需要使用 ODR)[引用选定的摘录,强调 我的]:

There can be more than one definition of a [...] static data member of a class template [...] in a program provided that each definition appears in a different translation unit, and provided the definitions satisfy the following requirements. Given such an entity named D defined in more than one translation unit, then

  • each definition of D shall consist of the same sequence of tokens; and

[...]

If the definitions of D satisfy all these requirements, then the behavior is as if there were a single definition of D. If the definitions of D do not satisfy these requirements, then the behavior is undefined.

有两个不同的定义D(在你的例子中,template1<int>::x"each definition of D shall consist of the same sequence of tokens" 没有实现,因此我们自然不可能实现 "[...] 就好像只有一个定义 D" 一样;因此 UB.