使用模板化类型和命名空间"无效使用不完整类型"

And*_*der 3 c++ templates namespaces c++11

以下是我正在使用的迭代器的简化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;
}
Run Code Online (Sandbox Code Playgroud)

代码在MSVC2015中编译良好,但在g ++ 4.8和clang 3.5(-std = c ++ 11)中失败,达到模板的最大递归深度.请注意,如果meta::meta_size_t替换为ns_size_t (并且ns_size_t取消注释的定义),那么一切正常.

我猜测编译器会延迟解析,meta::meta_size_t直到它完成解析,bar因为它在另一个命名空间(或者这些行中的某些东西),因此失败了,但我不确定如何解决这个问题.

在什么条件下这个问题通常会出现?有没有办法迫使编译器namespace meta's之前解析内容ns's?我想避免重复类型定义(例如ns_size_t).

进一步的上下文:在原始代码中,bar有一个std::tuple参数,并使用调用重载函数std::get<N>(tuple)

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

T.C*_*.C. 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是一个关联的命名空间,因此barADL找到第二个重载并通过重载决策选择,终止递归.

当您交换bars 的顺序时,普通的非限定查找将找到终止的情况,所以一切都很好.

MSVC因其在模板名称查找方面的不合格而闻名.