考虑我们有接受某种基本类型的函数:
void print(const base&);
然后,我们在另一个标头中定义了一个继承自base
的结构,称之为derived
。最后在第三个标题中,我们有print
的重载,它接受derived
。总的来说,我们有这个:
// header 1
struct base {};
void print(const base&);
// header 2
#include "header 1"
struct derived:base{};
// header 3
struct derived;
void print(const derived&);
重要的是要注意print
函数是在不同的TU中定义的。现在比较这两个TU:
// source 1
#include "header 2"
void foo() {
derived d;
print(d);
}
// source 2
#include "header 2"
#include "header 3"
void bar() {
derived d;
print(d);
}
对print
的调用看起来相同,但第一个源文件调用print(const base&)
,而第二个调用print(const derived&)
。这种行为是否合法并且由标准强制执行?还是我在某个时候闯入UB?
如果print
是模板函数,那么我们使用非模板方法重载print
(忽略可以在非模板上选择模板方法,因为它可能更匹配,假设我们正确地重载它),合法性会被改变吗?
问题的部分原因源于想知道什么是合法和合理的定制来源,什么不是。标准库倾向于对类模板进行部分/显式特化。
答案 0 :(得分:1)
这是合法的,但很脆弱。
如果您的foo
在标题中作为内联函数,则会出现问题,因为您可能会破坏ODR(一个定义规则)。
foo
会根据之前的内容致电void print(const base&);
或void print(const derived&);
。
foo
可能会被重写为:
void foo() {
derived d;
print(static_cast<Base&>(d));
}
明确选择哪个重载。
如果打印是模板函数,那么如果我们重载了打印
template <typename T> void print(const T&);
过载时,没有问题。
通过专精化,foo
和bar
将使用print<derived>
,但使用不同的定义。