我有这个嵌套的模板化结构:
template <typename... Ts>
struct A
{
template <unsigned i>
struct B
{
int b;
};
};
可以编译。
然后我尝试定义此功能。
template <unsigned i, typename... Ts>
void foo(A<Ts...>::B<i> var)
{
std::cout << var.b << std::endl;
}
由于某种原因,我不完全了解,因此无法编译。我必须按照以下方式对其进行更改才能使其正常工作。
template <unsigned i, typename... Ts>
void foo(typename A<Ts...>::template B<i> var)
{
std::cout << var.b << std::endl;
}
但是,当我这样称呼它时:
A<int, float>::B<0> var = {0};
foo(var);
它说不能推导模板参数i
。
我可以通过将模板参数显式添加到函数调用中来使其工作:
A<int, float>::B<0> var = {0};
foo<0, int, float>(var);
那是为什么?如何做到这一点,而不必在函数调用中指定模板参数?
答案 0 :(得分:4)
因为标准是这样
标准之所以这样说,是因为反转任意编译时间图等效于暂停。
这等同于暂停,因为模板元编程已完成图灵化。
这是图灵完整的,因为实际上很难制作出不是图灵完整的有用的编程语言。它是偶然发生的,避免使用它会导致一种语言,它需要对看似简单的事物进行恼人的解决方法。
因此,通常很难确定A
来自哪个A<int, float>::B<0>
并反转该映射的问题,因此C ++标准规定编译器不会尝试。
如果您编写自己的映射,则可以完成。首先,您必须意识到B
类型没有“头发”,因为B
的类型并没有将其附加到用于创建它的A
的类型上。我们可以添加“头发”:
template<class...Ts>
struct A {
template<unsigned i>
struct B {
using daddy = A;
int b;
};
};
现在我们可以从A
的类型中找到B<?>
。
接下来,我们添加一些逆映射:
template<class...Ts>
struct types_t {using type=types_t;};
template<class X>
struct get_types;
template<class X>
using get_types_t=typename get_types<X>::type;
template<template<class...>class Z, class...Ts>
struct get_types<Z<Ts...>>:types_t<Ts...>{};
template<class B>
struct get_B_index;
template<unsigned i, template<unsigned>class B>
struct get_B_index<B<i>>:std::integral_constant<unsigned, i>{};
从中我们可以从A
获得B
的模板参数:
template<class...Ts, unsigned i>
void foo( types_t<Ts...>, std::integral_constant<unsigned, i>, typename A<Ts...>::template B<i> var ) {
}
template<class B>
void foo( B b ) {
using types = get_types_t< typename B::daddy >;
using index = get_B_index< B >;
return foo( types{}, index{}, b );
}
答案 1 :(得分:2)
模板参数推导无法推断出嵌套名称说明符中出现的任何内容。例如,不允许从T
类型的参数推导typename Foo<T>::bar
。我认为,这样做的原因是通常不可能进行这种扣除。总是会有Foo
的局部特化将bar
定义为任意复杂的typedef。
解决方法是将嵌套类型定义为非嵌套类型,然后使用typedef
将其引入,但使用原始的非嵌套名称进行推导:
template <unsigned i, typename... Ts>
struct A_B { int b; }
template <typename... Ts>
struct A {
template <unsigned i> using B = A_B<i, Ts...>;
};
template <unsigned i, typename... Ts>
void foo(A_B<i, Ts...> var);
答案 2 :(得分:0)
在您的特定情况下,嵌套类型B
唯一地附加到封闭类型A
的特定专业领域。各种B
与它们所包围的A
之间存在一一对应的关系。因此,理论上应该可以从特定的A
推论B
的论点。
但是,在通常情况下,嵌套类型名称可能引用别名(例如typedef-name),而不是真正的新类型名称(如您的示例)。如果嵌套别名,则1:1对应关系不再成立,其他答案中的示例对此进行了证明。
语言规范无法区分这两种情况(真正的 new 嵌套类型与嵌套别名),因此它选择了“公共分母”方法,并且始终会处理封闭模板参数为非推断上下文。