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

"Invalid use of incomplete type" using templated type and namespaces

本文关键字:类型 无效 命名空间      更新时间:2023-10-16

以下是我在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和clang 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)

调用一个重载函数

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

基本规则是,当您编写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是一个关联的名称空间,因此ADL找到bar的第二个重载并由重载解析选择,从而终止递归。

当您交换bar s的顺序时,那么普通的非限定查找将找到终止情况,因此一切正常。

MSVC以其在模板名称查找方面的不一致性而闻名。