以下是模板上下文中非常奇怪的重载决策的极简主义示例:
#include <iostream>
// Types //
struct I { int v; };
template <class T>
struct D { T t; };
// Functions //
// Overload 1
template <class T>
I f(T) { return {1}; }
// Template indirection that calls f(T)
template <class T>
I g(D<T>) { return f(T{}); }
// Non template indirection that calls f(T)
I h(D<I>) { return f(I{}); }
int main() {
std::cout
<< f(I{}).v // f(I{}) overload called directly
<< "\n" // => overload 1 called
<< h(D<I>{}).v // f(I{}) called though non-template
<< "\n" // => overload 1 called
<< g(D<I>{}).v // f(I{}) called though template
<< "\n"; // => overload 2 actually called ???
}
// Overload 2
// Should not be reachable as declared after all f(...) calls.
// If was a candidate, would be chosen over 1.
I f(I) { return {2}; }
这似乎与ADL有关,因为如果将I
放在命名空间中,则始终会调用“overload 1”。
我知道ADL的执行就像调用模板实例化点(main
)一样。
对于模板定义中使用的依赖名称,查询将被推迟,直到模板参数已知,此时ADL检查具有外部链接的函数声明(直到C ++ 11),这些声明从模板定义上下文可见以及在模板实例化上下文中,而非ADL查找仅检查从模板定义上下文可见的外部链接(直到C ++ 11)的函数声明。 http://en.cppreference.com/w/cpp/language/unqualified_lookup#Template_definition
但是这里“Overload 2”在main
之后宣布! main
是g
和f
的实例点,我假设只有在 main
之前声明的函数才会超载候选。
请注意,此行为与作为模板的g
相关,因为h
(g
的等效而非模板函数)调用“Overload 1”。
如何在main
之后声明“重载2” - 是否会被调用?
使用clang ++(3.8.1)和g ++(6.2.1)重现了这种行为。
答案 0 :(得分:5)
如果电话会形成不良或者会找到更好的匹配 在关联的命名空间内查找考虑了所有函数 在这些名称空间中引入外部链接的声明 所有翻译单位,而不仅仅是考虑那些发现的声明 然后,在模板定义和模板实例化上下文中 该程序有不确定的行为。
此外,[temp.point]/6,强调我的:
功能模板的特化[...]可能有多个 翻译单元内的实例化点,以及 对于任何此类实例,上述实例化的要点 具有实例化点的专业化 翻译单位,翻译单元的结尾也被认为是 实例化点。 [...]如果有两个不同的实例化点 根据标准给出模板专业化的不同含义 一个定义规则([basic.def.odr]),程序格式不正确,没有 需要诊断。
程序的行为未定义。