这个问题是在这个答案的背景下出现的。
正如我所料,这个翻译单元不能编译:
template <int Num> int getNum() { return Num; }
template int getNum<0>();
template int getNum<0>(); // error: duplicate explicit instantiation of 'getNum<0>'
int main() { getNum<0>(); return 0; }
我理解这一点,我曾尝试两次进行相同的显式模板实例化。然而,事实证明,将其分成不同的单元,它编译:
// decl.h
template <int Num> int getNum() { return Num; }
// a.cc
#include <decl.h>
template int getNum<0>();
// b.cc
#include <decl.h>
template int getNum<0>();
int main() { getNum<0>(); return 0; }
我没想到会这样。我假设具有相同参数的多个显式模板实例化会破坏 ODR,但似乎并非如此。然而,这确实失败了:
// decl.h
template <int Num> int getNum();
// a.cc
#include "decl.h"
template <> int getNum<0>() { return 0; }
// b.cc
#include "decl.h"
template <> int getNum<0>() { return 0; }
int main() { getNum<0>(); return 0; }
用户Oliv帮助我指出了标准中的相关段落,但我仍然对此感到有些困惑,所以我希望有人可以用更简单的术语解释这背后的逻辑(例如,什么应该或不应该被认为是破坏 ODR以及为什么我的期望是错误的)。
编辑:
再举一个例子,这是一个分为两个单元的程序,可以正确编译,但会产生令人惊讶的结果:
// a.cc
template <int Num> int getNum() { return Num + 1; }
template int getNum<0>();
// b.cc
#include <iostream>
template <int Num> int getNum() { return Num; }
template int getNum<0>();
int main() { std::cout << getNum<0>() << std::endl; return 0; }
输出:
1
在这种情况下,删除显式模板实例化会产生0
. 我知道拥有两个具有不同定义的模板并不是一个常见的用例,但我认为 ODR 是为了避免此类问题而精确实施的。