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

Ton*_*ous 4 c++ linker templates g++

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

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

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

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

my_template.h

template <class T>
class template1
{
public:
    static int x;
};
Run Code Online (Sandbox Code Playgroud)

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.
}
Run Code Online (Sandbox Code Playgroud)

主程序

#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;
}
Run Code Online (Sandbox Code Playgroud)

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

编译1

g++ -o prog Src2.cpp Main.cpp
Run Code Online (Sandbox Code Playgroud)

输出 1

main: template1<int>::x = 2
my_func: template1<int>::x = 2
my_func: template1<char>::x = 2
Run Code Online (Sandbox Code Playgroud)

编译2

g++ -o prog Main.cpp Src2.cpp
Run Code Online (Sandbox Code Playgroud)

输出 2

main: template1<int>::x = 1
my_func: template1<int>::x = 1
my_func: template1<char>::x = 2
Run Code Online (Sandbox Code Playgroud)

也观察到

Microsoft (R) C/C++ Optimizing Compiler Version 19.25.28612 for x86
Run Code Online (Sandbox Code Playgroud)

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

Nightra 合作

cig*_*ien 5

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

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