冲突的模板定义和 ODR

Conflicting template definitions and ODR

想象一下我有两个不同的翻译单元的情况a.cpp

#include <iostream>

double bar();

template <typename T>
T foobar(T t) {
    return t;
}

int main() {
    std::cout << "foobar called from b.cpp: " << bar() << '\n';
    std::cout << "foobar called from a.cpp: " << foobar(1.) << '\n';
}

和b.cpp:

template <typename T>
T foobar(T t) {
    return t + 1.;
}

double bar() {
    return foobar(1.);
}

我知道对于模板,ODR 有例外,即编译器将标记实例化的函数模板,并在链接过程中删除除一个之外的所有模板。我注意到编译器实际上并不关心在不同翻译单元上生成的此类实例化代码是否实际上相同或至少相同。

在上面的代码中,就是这样的。编译、链接和运行

c++ a.cpp b.cpp -o result -std=c++17 && ./result

它将产生结果

foobar called from b.cpp: 1
foobar called from a.cpp: 1

很明显,目标文件 b.o 中的实例化被丢弃了,取而代之的是 a.o 中的实例化。编译和链接 b.cpp 和 a.cpp 交换时,如

c++ b.cpp a.cpp -o result -std=c++17 && ./result

结果会是

foobar called from b.cpp: 2
foobar called from a.cpp: 2

所以恰恰相反:在要链接的目标文件列表中首先提到的实例化将是幸存下来的。这种行为是否在标准中的某处定义?根据构建系统的不同,提及目标文件的顺序可以是任意的,但是,在这样的示例中,它会导致非常不同的程序,并可能导致麻烦的错误。即使我尝试通过添加

从 a.cpp 显式实例化版本
template double foobar<double>(double);

当在链接器列表中的 a.o 之前提到 b.o 时,a.cpp 中的 foobar<> 模板将不会存活。

I know that for templates, there are exceptions to the ODR

模板的ODR没有例外,只是模板函数是inline

并且您的程序违反了 ODR。
常规内联函数也会有类似的问题。