2015-08-15 99 views
3

以下是我在std::tuple上使用的迭代器的简化。在这里面,我在一个名称空间定义一个模板类型,并且在第二个使用它,例如,如下所示:使用模板类型和名称空间的“不完整类型的使用无效”

namespace meta{ 
    template<size_t> struct meta_size_t {};  
} 

namespace ns{ 

    //template<size_t> struct ns_size_t {};  

    template <size_t N> 
    void bar(meta::meta_size_t<N>) { 
     bar(meta::meta_size_t<N-1>()); 
    } 
    void bar(meta::meta_size_t<1>) {} 

    template<size_t N> 
    void foo() { 
     bar(meta::meta_size_t<N>()); 
    } 
} 

int main(void){ 
    ns::foo<5>(); 
    return 0; 
} 

代码编译在MSVC2015细,但未能以g ++ 4.8和铛3.5(-std = C++ 11)通过达到模板的最大递归深度。请注意,如果meta::meta_size_t被替换为ns_size_t(并且ns_size_t的定义未被注释),那么一切正常。

我推测,编译器延缓meta::meta_size_t分辨率,直到它完成解决bar,因为它是在另一个命名空间(或东西沿着这些线路),并因此失败了,但我不知道如何解决这个问题。

在什么情况下会出现这个问题?有没有办法强制编译器在ns's之前解析namespace meta's的内容?我想避免重复类型定义(以ns_size_t为例)。

此外上下文:在原始代码,bar具有std::tuple参数,并进行调用以使用std::get<N>(tuple)

编辑一个重载的函数: Noobish错误。查看@WhozCraig提供的示例后,我验证了可以通过交换两个bar方法的顺序来修复代码(我假定g ++和clang按顺序搜索定义,因此永远不会注册第二个过载条,而MSVC必须在继续之前将所有定义添加到其符号表中)。标准是指定一种方法还是另一种方法,还是具体实现?也就是说,如果定义不在命名空间内,我不明白为什么这不是问题。

+3

第二个'bar'应该是专业吗?无论如何,你可以转发 - 声明模板,实现重载,然后实现实际的递归模板,并且它应该被编译。 [见它活着](http://ideone.com/2OQJ2G)。 – WhozCraig

+1

您的模板递归**是**无限的。 '虚空栏(... ...)'需要'无效栏(... ...),永远。 –

+0

@WhozCraig第二个栏是元组迭代器的停止条件(从某种意义上说,是的,专业化,但不符合将代码调整为特定类型的传统意图,而仅仅是作为控制流的手段) –

回答

5

基本规则是当您编写foo(/* something dependent on a template parameter*/);时,foo的普通非限定查找仅考虑模板定义上下文,而ADL将同时考虑定义和实例化上下文。因此,在模板定义上下文中找不到的重载的唯一方法是ADL。

bar(meta::meta_size_t<N-1>());void bar(meta::meta_size_t<1>) {}不在范围内,因此它可以被称为唯一的办法就是通过参数相关的查找,但ns不是meta::meta_size_t<1>相关的命名空间,所以不是你得到无限递归。

当您使用ns_size_t时,那么ns是一个关联的命名空间,因此bar的第二个重载由ADL找到,并通过重载决议选择,终止递归。

当您交换bar s的顺序时,普通的不合格查找会找到终止的情况,因此一切正常。

MSVC因其在模板名称查找领域的不合格而众所周知。

相关问题