c++ - 为什么在通过模板进行静态调度时不需要前向声明?

标签 c++ templates static-polymorphism

我正在玩一些静态多态性,我正在调用一个函数,该函数在内部根据初始参数的类型调用“正确的”专用函数(基本上我正在做标记)。代码如下:

#include <iostream>

using namespace std;

// tags
struct tag1{}; 
struct tag2{}; 

// the compliant types, all should typedef tag_type
struct my_type1
{
    using tag_type = tag1;
};
struct my_type2
{
    using tag_type = tag2;
};

// static dispatch via tagging
template <typename T>
void f(T) 
{
    cout << "In void f<typename T>(T)" << endl;

    // why can I call f_helper without forward definition?!?        
    f_helper(typename T::tag_type{}); 
}

int main()
{
    my_type1 type1;
    my_type2 type2;

    // how does f below knows about f_helper ?!?!
    // even after instantiation f_helper shouldn't be visible!

    f(type1); 
    f(type2);
}

// helper functions
void f_helper(tag1) 
{
    cout << "f called with my_type1" << endl;
}
void f_helper(tag2)
{
    cout << "f called with my_type2" << endl;
}

所以,f(T) 是使用参数 my_type1my_type2 调用的,该参数在内部必须 typedef tag_type使用适当的标签 tag1/tag2。根据这个内部的tag_type,“正确的”包装器会被调用,这个决定当然是在编译时做出的。现在我真的不明白为什么这段代码有效?为什么我们不需要前向声明 f_helper?我首先在 main 之前(以及在 f 之后)定义了包装器,虽然没关系,但你不需要转发声明,因为编译器会实例化模板仅在调用 f(type1); 时(在 main() 中),在它不知道类型 T 之前,所以在编译器知道 f_wrapper 的实例化时间。

但正如您所见,即使我在 main() 之后声明了包装器,代码仍然有效。为什么会这样?我想这个问题有点奇怪,问为什么代码有效:)


编辑

即使在 gcc5 和 gcc HEAD 6.0.0 中,代码也会继续编译。

最佳答案

f_helper(typename T::tag_type{})是一个依赖于类型的表达式,因为 T::tag_type是依赖类型。这意味着 f_helperf<T> 之前不需要可见由于两阶段查找而被实例化。

编辑:我很确定这实际上是未定义的行为。如果我们查看 14.6.4.2 [temp.dep.candidate],我们会看到以下段落:

For a function call that depends on a template parameter, the candidate functions are found using the usual lookup rules (3.4.1, 3.4.2, 3.4.3) except that:

— For the part of the lookup using unqualified name lookup (3.4.1) or qualified name lookup (3.4.3), only function declarations from the template definition context are found.

— For the part of the lookup using associated namespaces (3.4.2), only function declarations found in either the template definition context or the template instantiation context are found.

If the function name is an unqualified-id and the call would be ill-formed or would find a better match had the lookup within the associated namespaces considered all the function declarations with external linkage introduced in those namespaces in all translation units, not just considering those declarations found in the template definition and template instantiation contexts, then the program has undefined behavior.

对我来说,最后一段表明这是未定义的行为。 function call that depends on a template parameter这里是 f_helper(typename T::tag_type{}) . f_helperf 时不可见是实例化的,但如果我们在编译所有翻译单元之后执行名称查找。

关于c++ - 为什么在通过模板进行静态调度时不需要前向声明?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/25188353/

相关文章:

c++ - 你能从 volatile 函数中去掉成员的 volatile 吗?

C++ 模板和继承

c++ - 为什么用 debug_rep(&s) 调用模板 <typename T> string debug_rep(T *p) 时 T 不是 string*

c++ - 在编译时查找基类

c++ - 仅分析我需要使用 VS2010 的命名空间

c++ - CRTP 中的模板化派生类(奇怪的重复模板模式)

c++ - 解释了一种将 double 舍入为 32 位整数的快速方法

c++ - 将静态访问者与静态多态性层次结构耦合

c++ - CRTP - 我可以创建一个私有(private)方法吗?