模板静态成员定义取决于传递给链接器的顺序
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.
下面的代码,其中有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 ofD
. If the definitions ofD
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.