【发布时间】:2018-10-05 11:03:01
【问题描述】:
这个问题是在this answer的上下文中出现的。
正如我所料,这个翻译单元无法编译:
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 很有帮助地将我指向this relevant paragraph in the standard,但我仍然对此感到有些困惑,所以我希望有人能用更简单的术语解释这背后的逻辑(例如,应该或不应该考虑什么打破 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 的实施正是为了避免此类问题。
【问题讨论】:
-
AFAIK,除了前者的不可见性之外,隐式和显式实例化之间没有区别。
-
是的,模板实例化是弱符号,所以链接器只选择它感觉的任何一个。不过不太确定,因此不是答案。
-
@molbdnilo 感谢您的评论。我明白你的意思,但我不相信为什么会这样。我添加了一个(复杂的)案例,其中显式初始化会产生意想不到的结果。
-
“每个程序都应包含一个定义,该定义在该程序中被丢弃的语句之外的 odr 使用的每个非内联函数或变量;不需要诊断。”
-
强/弱符号超出了 c++ 标准。这样做是为了不强制编译器检查,IMO,因为在一般情况下检查每个 ODR 违规可能会很复杂。
标签: c++ one-definition-rule explicit-instantiation