为什么在类声明中定义时允许类模板成员函数的显式特化,而不是在单独定义时?

时间:2012-06-18 15:00:55

标签: c++ templates

我看过很多StackOverflow帖子,说C ++中的类模板成员函数的特化是不合法的,除非封闭类也是专用的。

然而,另一篇文章似乎表明 可以专门化成员模板函数,只要专门函数的定义出现在模板类的类声明中,如下所示:

template<typename T1, typename T2>
class A
{
public:
    template<typename TX> TX foo() {return 0;};
    template<> T1 foo<T1>() {return 1;}; // provide the specialization's definition within the class declaration: works
    //template<> T1 foo<T1>(); // ... switch to this (and provide the definition below), and it's a compile error
};

// This function definition is only present when the commented-out line of code in the class declaration is used
template<typename T1, typename T2>
template<>
T1 A<T1, T2>::foo<T1>()
{
    return 1;
}

int main(void)
{
    A<int, double> a;
    const int n = a.foo<int>(); // When the definition appears inside the class declaration, n is 1 - it works
    return 0;
}

从我的代码示例中的注释中可以看出,当在类声明中提供专用函数的定义时,代码构建没有错误,而且它成功运行,并且n的值在函数中正如预期的那样,main()1

但是,当在类声明之外移动专用函数定义时,如果保持相同,如图所示,编译器会发出其他帖子已经指出的错误;就我而言(Visual Studio 2010),错误是an explicit specialization of a template member must be a member of an explicit specialization

我的问题是这个。如果不允许在类模板中对成员函数进行显式特化,除非封闭模板类也是专用的,那么如果在类声明中提供了定义,为什么它在我的示例代码中有用呢?

1 个答案:

答案 0 :(得分:3)

两个版本都不应该出于不同的原因进行编译。

在第一种情况下,必须在命名空间级别存在特化,这意味着编译器应该拒绝在类定义中提供的特化。

在第二种情况下,专门化的位置是正确的,但是为了提供成员函数的特化(函数的特化必须始终是完整的,从不局部),您还需要修复封闭的模板类参数。也就是说,无论使用哪种类型fooX来实例化封闭模板,您都无法对T1类型进行专门化T2。您被允许(可能不是您想要的)提供所有已修复参数的专业化:

template <> template <>
int A<int, double>::foo<int>() { return 5; }

您可以使用的替代方案是:

  • 提供非模板重载,而不是模板特化(在大多数情况下,提供重载比特化更有意义,但这可能不是这种情况,因为你不能在返回类型上重载。
  • 将该函数移动到另一个模板类,然后在那里执行部分特化。
  • 执行第一个选项的变体:提供单个非专用模板供客户端使用,转发到提供不同重载的内部函数

示例:

// inside class template:
private:
   template <typename T> T foo_impl( T* _ ) {...} // generic
   T1 foo_impl( T1 * _ ) {...}                    // concrete
public:
   template <typename T> T foo() {
      return foo_impl( (T*)0 );
   }

这样你只能将返回类型的模板转换为对重载函数的调用,并且你可以让编译器为你解决问题。