特化此函数模板时不能省略模板参数

Template arguments can't be omitted when specializing this function template

在 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

也许编译器认为typename标记的类型(deduce<float>::type)不如普通类型可靠?

Maybe the compiler think the type(deduce<float>::type) marked by typename is less reliable than normal types ?

typename没有任何关系,重点是deduce<T>::...是nested-name-specifier;属于 Non-deduced contexts:

(强调我的)

In the following cases, the types, templates, and non-type values that are used to compose P do not participate in template argument deduction, but instead use the template arguments that were either deduced elsewhere or explicitly specified. If a template parameter is used only in non-deduced contexts and is not explicitly specified, template argument deduction fails.

1) The nested-name-specifier (everything to the left of the scope resolution operator ::) of a type that was specified using a qualified-id:

所以,对于

template  <>
typename deduce<float>::type fun1()

deduce<float>::type(即float*)将用于为deduce<T>::type推导类型T,但不会推导T,模板参数推演失败。您必须将其明确指定为 float.

让我举例说明为什么非推导上下文是非推导的。模板推导基本上是在尝试匹配输入。如果我有:

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 typeint。我们如何找到这样的东西?

现在假设我们有这个:

template <class T> struct X { using type = T; };

好了,现在很简单了吧? T=int?好吧,没那么快。对于主模板,在这种情况下会起作用。但是,如果还有这种专业化怎么办:

template <class T> struct X<T*> { using type = T; };

(即 Xstd::remove_pointer)。现在我们处于 T=int 有效的情况......但 T=int* 也有效。也许还有一些其他类型也适用于 int。如何选择合适的?

这个问题 - 在 qualified-id 的嵌套名称说明符中选择模板参数 - 真的很难,没有明显的前进方向。所以编译器不会采取前进的道路。这是一个非推导的上下文。 T 永远不会在对 baz 的调用中推导出来,调用者必须提供它:

baz<int>(4); // ahhhhh, ok, you wanted X<int>::type

回到你的问题。 some_struct<T> 是推导上下文,但 typename deduce<T>::type 是非推导上下文。我希望现在很清楚为什么前者有效而后者无效。