以下是我在 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
因为它在另一个 namespace (或类似的东西)中,因此失败了,但我不确定如何解决这个问题。
这个问题一般在什么情况下出现?有没有办法强制编译器解析 namespace meta's
ns's
之前的内容?我想避免重复类型定义(如 ns_size_t
所示)。
更多上下文:在原始代码中,bar
有一个 std::tuple
参数,并使用 std::get<N>(tuple)
调用重载函数
编辑: 愚蠢的错误。查看@WhozCraig 提供的示例后,我验证了可以通过交换两个 bar
的顺序来修复代码。方法(我假设 g++ 和 clang 按顺序搜索定义,因此永远不会注册 bar 的第二个重载,而 MSVC 必须在继续之前将所有定义添加到其符号表中)。该标准是否指定了一种方法或另一种方法,或者此实现是否特定?也就是说,如果定义不在 namespace 内,我不明白为什么这不是问题。
最佳答案
基本规则是当你写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 以其在模板名称查找方面的不规范而闻名。
关于c++ - "Invalid use of incomplete type"使用模板化类型和命名空间,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/32021189/