为什么不需要通过模板静态调度的向前声明? [英] why no need of forward declaration in static dispatching via templates?

查看:144
本文介绍了为什么不需要通过模板静态调度的向前声明?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我使用静态多态性,我调用一个函数,内部调用正确的专门的函数,取决于初始参数的类型(基本上我在做标记)。这里是代码:

  #include< iostream> 

using namespace std;

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

//符合类型,应该是typedef tag_type
struct my_type1
{
使用tag_type = tag1;
};
struct my_type2
{
使用tag_type = tag2;
};

//通过标记进行静态分派
template< typename T>
void f(T)
{
cout<< In void f< typename T>(T)<< endl;

//为什么我可以调用f_helper没有向前定义?
f_helper(typename T :: tag_type {});
}

int main()
{
my_type1 type1;
my_type2 type2;

//下面f怎么知道f_helper?!?!
//即使在实例化之后f_helper也不应该可见!

f(type1);
f(type2);
}

//帮助函数
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_type1 my_type2 调用,内部必须typedef tag_type 与适当的标记 tag1 / tag2 。根据这个内部 tag_type ,然后调用右包装,这个决定当然是在编译时做出的。现在我真的不明白为什么这个代码是工作?为什么我们不需要转发声明 f_helper ?我首先在 main (和 f 之后)定义了包装器,但我确定,不需要转发声明,因为只有当 f(type1); (在 main()),在它不知道类型 T 之前,因此在实例化时编译器知道 f_wrapper



但是正如你所看到的,即使我声明了包装器AFTER main(),代码仍然可以工作。为什么会发生这种情况?我想问题有点奇怪,问为什么一个代码的工作原理:






EDIT



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

解决方案

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



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


对于依赖于模板的函数调用参数,使用通常的查找规则(3.4.1,
3.4.2,3.4.3)找到
候选函数,除了:



- 对于使用非限定名称查找(3.4.1)或限定名称查找(3.4.3)的查找部分,仅找到来自模板定义上下文的
函数声明。



- 对于使用关联命名空间(3.4.2)的查找部分,只找到
在模板定义上下文
或模板实例化上下文中找到的函数声明。 / p>

如果函数名
是一个非限定id,调用将会失败或找到一个
更好的匹配,关联命名空间
考虑了在所有翻译单元中在这些命名空间中引入的外部链接
的所有函数声明,而不仅仅是
考虑在模板定义中找到的那些声明和
模板实例化上下文,那么程序有未定义的
行为。


我最后一段表示这是未定义的行为。依赖于模板参数函数调用这里是 f_helper(typename T :: tag_type {}) f_helper 在实例化 f 时不可见,但是如果我们在所有翻译单元已编译。


I am playing a bit with static polymorphism, I'm calling a function which internally calls the "right" specialized function depending on the type of the initial argument (basically I'm doing tagging). Here is the code:

#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;
}

So, f(T) is called with a parameter my_type1 or my_type2 that internally must typedef tag_type with the appropriate tag tag1/tag2. Depending on this internal tag_type, the "right" wrapper is then called, and this decision is made of course at compile time. Now I really don't understand why this code IS working? Why don't we need to forward-declare f_helper? I first had the wrappers defined before main (and after f), and I though ok, this makes sense, you don't need to forward declare because the compiler instantiate the template only when f(type1); is called (in main()), before it doesn't know the type T, so at the time of instantiation the compiler knows f_wrapper.

But as you see, even if I declare the wrappers AFTER main(), the code still works. Why is this happening? I guess the question is a bit strange, asking why a code works :)


EDIT

The code continues to compile even in gcc5 and gcc HEAD 6.0.0.

解决方案

f_helper(typename T::tag_type{}) is a type-dependent expression because T::tag_type is a dependent type. This means that f_helper doesn't need to be visible until f<T> is instantiated due to two phase lookup.

EDIT: I'm pretty sure that this is actually undefined behaviour. If we look at 14.6.4.2 [temp.dep.candidate] we see this passage:

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.

The last paragraph to me indicates this is undefined behaviour. The function call that depends on a template parameter here is f_helper(typename T::tag_type{}). f_helper isn't visible when f is instantiated, but it would be if we performed name lookup after all translation units have been compiled.

这篇关于为什么不需要通过模板静态调度的向前声明?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆