从模板实例化后声明的模板函数选择候选者调用C ++模板重载决策

时间:2016-10-07 21:09:25

标签: c++ templates overload-resolution

以下是模板上下文中非常奇怪的重载决策的极简主义示例:

#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之后宣布! maingf的实例点,我假设只有在 main之前声明的函数才会超载候选。

请注意,此行为与作为模板的g相关,因为hg的等效而非模板函数)调用“Overload 1”。

如何在main之后声明“重载2” - 是否会被调用?

使用clang ++(3.8.1)和g ++(6.2.1)重现了这种行为。

1 个答案:

答案 0 :(得分:5)

[temp.dep.candidate]/1

  

如果电话会形成不良或者会找到更好的匹配   在关联的命名空间内查找考虑了所有函数   在这些名称空间中引入外部链接的声明   所有翻译单位,而不仅仅是考虑那些发现的声明   然后,在模板定义和模板实例化上下文中   该程序有不确定的行为。

此外,[temp.point]/6,强调我的:

  

功能模板的特化[...]可能有多个   翻译单元内的实例化点,以及   对于任何此类实例,上述实例化的要点   具有实例化点的专业化   翻译单位,翻译单元的结尾也被认为是   实例化点。 [...]如果有两个不同的实例化点   根据标准给出模板专业化的不同含义   一个定义规则([basic.def.odr]),程序格式不正确,没有   需要诊断。

程序的行为未定义。