【发布时间】:2015-08-15 03:21:52
【问题描述】:
以下是我在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) 调用重载函数
编辑: Noobish 错误。在查看了@WhozCraig 提供的示例后,我验证了可以通过交换两个bar 方法的顺序来修复代码(我假设 g++ 和 clang 按顺序搜索定义,因此永远不会注册第二个重载条,而 MSVC 必须在继续之前将所有定义添加到其符号表中)。标准是否指定了一种方法或另一种方法,或者此实现是特定的?也就是说,如果定义不在命名空间内,我不明白为什么这不是问题。
【问题讨论】:
-
第二个
bar应该是专业吗?无论如何,您可以前向声明模板,实现重载,然后实现实际的递归模板,它应该可以编译。 See it live. -
你的模板递归是无限的。
void bar(...<N>...)需要void bar(...<N-1>...),永远。 -
@WhozCraig 第二个条形图是元组迭代器的停止条件(从某种意义上说,是的,一种特殊化,但不是按照将代码裁剪为特定类型的传统意图,而只是作为一种手段控制流)
-
@DrewDormann 是,除了 bar,它不(不应该)引用 bar
-
我知道这是停止条件。我只是期望专业化而不是超载。由于非类型模板 arg 的性质,这里不需要,但让我回过神来。
标签: c++ templates c++11 namespaces