在C ++中,函数模板的显式特化如下:
template<typename T> return_type fun_name();
template<> return_type fun_name<int>(){/* blabla */}
上例中的<int>
称为模板参数。有时候<int>
可以省略,因为编译器可以执行Template Argument Deduction
但我无法在下面的示例中找出Template Argument Deduction失败的原因:
//-------------failed case-------------
template <typename T>
struct deduce{
typedef T* type;
};
template <typename T>
typename deduce<T>::type fun1();
template <>
typename deduce<float>::type fun1<float>() //error if no "<float>" after fun1
{
}
//------------now the "Template Argument Deduction" works------------
template <typename T>
struct some_struct{
T* p;
};
template <typename T>
some_struct<T> fun2();
template <>
some_struct<float> fun2() // no error even if no "<float>" after fun2
{
}
如果在fun1之后没有<float>
,则错误消息为:
error: template-id ‘fun1<>’ for ‘float* fun1()’ does not match any template declaration
也许编译器认为deduce<float>::type
标记的类型(typename
)不如普通类型可靠?
答案 0 :(得分:1)
让我举一个例子说明为什么非推断的上下文是非推断的。模板推导基本上是试图匹配输入。如果我有:
template <class T> void foo(T );
我致电foo(4)
,这很容易。 T=int
。如果我拨打foo('x')
,T=char
。这些都很容易取代。如果T
嵌套在类型中的某个位置,例如:
template <class T> void bar(std::vector<T> );
这仍然是完全可行的。如果我使用std::vector<std::vector<float>>
,T=std::vector<float>
来呼叫它。仍然没问题。
现在考虑一下:
template <class T> void baz(typename X<T>::type );
baz(4);
T
是什么?在我们之前的所有情况中,T
有一个明显的选择,它是从传递给函数模板的参数中直接推导出来的。但在这里,事实并非如此。我们有一个额外的间接层 - 我们需要推导出一个T
来创建一个类型X<T>
,其成员typedef type
是int
。我们怎么找到这样的东西?
现在我们说我们有这个:
template <class T> struct X { using type = T; };
好的,现在很容易吗? T=int
?好吧,不是那么快。对于主模板,在这种情况下可以使用。但如果还有这种专业化怎么办:
template <class T> struct X<T*> { using type = T; };
(即X
为std::remove_pointer
)。现在我们处于T=int
工作的情况......但T=int*
也有效。也许还有一些其他类型也适用于int
。你怎么选择合适的?
这个问题 - 在qualified-id的nested-name说明符中选择一个模板参数 - 真的很难,没有明显的前进路径。所以编译器只是没有采取前进的道路。这是一个非演绎的背景。在T
的来电中永远不会推断出baz
,来电者必须提供它:
baz<int>(4); // ahhhhh, ok, you wanted X<int>::type
回到你的问题。 some_struct<T>
是推断的上下文,但typename deduce<T>::type
是非推断的上下文。我希望现在清楚为什么前者有效,但后者没有。
答案 1 :(得分:0)
也许编译器认为
deduce<float>::type
标记的类型(typename
)不如普通类型可靠?
它与typename
无关,关键是deduce<T>::...
是嵌套名称说明符;属于Non-deduced contexts:
(强调我的)
在下列情况下,用于撰写P 的类型,模板和非类型值不参与模板参数推导,而是使用在其他地方推导出的模板参数或明确指定。 如果模板参数仅在非推导的上下文中使用且未明确指定,则模板参数推断失败。
1)嵌套名称说明符(范围解析运算符左侧的所有内容::)使用qualified-id指定的类型:
所以,对于
template <>
typename deduce<float>::type fun1()
deduce<float>::type
(即float*
)将用于推导T
的{{1}}类型,但deduce<T>::type
将不会推断,模板参数扣除失败。您必须明确指定为T
。