我保证不被此ODR违规行为所咬伤吗?

Fre*_*eMe 5 c++ linker templates one-definition-rule

我有一个头文件,声明一个带有静态变量的模板,并定义它:

/* my_header.hpp */
#ifndef MY_HEADER_HPP_
#define MY_HEADER_HPP_

#include <cstdio>

template<int n>
struct foo {
    static int bar;

    static void dump() { printf("%d\n", bar); }
};

template<int n>
int foo<n>::bar;

#endif // MY_HEADER_HPP_
Run Code Online (Sandbox Code Playgroud)

此标头包含在main.cpp共享库中mylib.特别是,mylib_baz.hpp只需包含此模板并声明一个修改模板特化的函数.

/* mylib_baz.hpp */
#ifndef MYLIB_BAZ_HPP_
#define MYLIB_BAZ_HPP_

#include "my_header.hpp"

typedef foo<123> mylib_foo;
void init_mylib_foo();

#endif // MYLIB_BAZ_HPP_
Run Code Online (Sandbox Code Playgroud)

/* mylib_baz.cpp */
#include "mylib_baz.hpp"
void init_mylib_foo() {
    mylib_foo::bar = 123;
    mylib_foo::dump();
};
Run Code Online (Sandbox Code Playgroud)

当我生成mylib.so(包含mylib_baz.o)时,符号for foo<123>::bar存在并声明为弱.但是,我的符号for也foo<123>::bar被宣布为弱main.o:

/* main.cpp */
#include "my_header.hpp"
#include "mylib_baz.hpp"

int main() {
    foo<123>::bar = 456;
    foo<123>::dump(); /* outputs 456 */
    init_mylib_foo(); /* outputs 123 */
    foo<123>::dump(); /* outputs 123 -- is this guaranteed? */
}
Run Code Online (Sandbox Code Playgroud)

我似乎违反了一个定义规则(foo<123>::barmy_header.cpp和中定义main.cpp).但是,使用g ++和clang这两个符号都被声明为弱(或唯一),所以我不会被这个 - 所有访问foo<123>::bar修改同一个对象.

问题1:这是巧合(对于模板的静态成员,可能ODR的工作方式不同吗?)或者我实际上是否通过标准保证了这种行为?

问题2:我怎么能预测我正在观察的行为?那是什么让编译器声明符号弱?

Bar*_*rry 1

不存在 ODR 违规行为。你对的定义只有一个bar。是这里:

template<int n>
int foo<n>::bar; // <==
Run Code Online (Sandbox Code Playgroud)

bar原样static,这表明所有翻译单元都有一个定义。尽管bar它将在所有目标文件中显示一次(毕竟它们需要一个符号),链接器会将它们合并在一起,成为bar. 你可以看到:

$ g++ -std=c++11 -c mylib_baz.cpp -o mylib_baz.o
$ g++ -std=c++11 -c main.cpp -o main.o
$ g++ main.o mylib_baz.o -o a.out
Run Code Online (Sandbox Code Playgroud)

生产:

$ nm mylib_baz.o | c++filt | grep bar
0000000000000000 u foo<123>::bar
$ nm main.o | c++filt | grep bar
0000000000000000 u foo<123>::bar
$ nm a.out | c++filt | grep bar
0000000000601038 u foo<123>::bar
Run Code Online (Sandbox Code Playgroud)

其中u表示:

“u”
符号是全球独一无二的符号。这是 ELF 符号绑定标准集的 GNU 扩展。对于这样的符号,动态链接器将确保在整个过程中只有一个具有该名称和类型的符号正在使用。