专门化此功能模板时,不能省略模板参数

时间:2016-10-28 00:28:17

标签: c++ templates template-specialization function-templates

在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)不如普通类型可靠?

2 个答案:

答案 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 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的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