首页 > 解决方案 > 模板类中的模板成员特化

问题描述

#include <iostream>
#include <string>

template<typename U>
struct A
{
  template<typename... Ts> auto func();
  template<> auto func<int>();
};

template<typename U>
template<>
auto
A<U>::func<int>() { return std::string{"foo"}; }

int main()
{
  A<float> a{}; 
  std::cout << a.func<int>() << std::endl;
}

这不起作用,因为模板类的模板成员的专门化是不可能的,除非您也专门化该类。(我读到过。)

但是如果将成员特化的定义移到类定义中,它确实有效:

#include <iostream>
#include <string>

template<typename U>
struct A
{
  template<typename... Ts> auto func();
  template<> auto func<int>() { return std::string{"foo"}; }
};

int main()
{
  A<float> a{};
  std::cout << a.func<int>() << std::endl;
}

我不确定我是否完全理解为什么。此外,当它使用 clang 时,它不能使用 gcc 编译。那么哪一个是对的呢?

但我真正的问题是,假设 clang 是正确的,为什么这又不起作用:

#include <iostream>
#include <string>

template<typename U>
struct A
{
  template<typename... Ts> auto func();
  template<> auto func<U>() { return std::string{"foo"}; }
};

int main()
{
  A<int> a{};
  std::cout << a.func<int>() << std::endl;
}

这是一个不同的错误,不是非特化模板成员的特化,而是抱怨是func<int>在定义之前不能使用推导的返回类型。

标签: c++templates

解决方案


如果我们看n4810 § 13.8.3

  1. 成员函数、成员函数模板、成员类、成员枚举、成员类模板、静态数据成员或类模板的静态数据成员模板可以显式特化为隐式实例化的类特化;在这种情况下,类模板的定义应在类模板成员的显式特化之前。如果类模板成员的这种显式特化命名了一个隐式声明的特殊成员函数(11.3.3),则程序格式错误。

但是,您可以这样做,其中两者都是专门的:

template<typename U>
struct A
{
  template<typename... Ts> auto func() { /* ... */ }
};

template <>
template <>
auto A<int>::func<int>()
{
  // ok, enclosing class template explicitly specialized
}

虽然这是无效的 C++:

template <typename U>
template <>
auto A<U>::func<int>()
{
  // ops: invalid, enclosing class template not explicitly specialized
}

根据:

  1. 在类模板的成员或出现在命名空间范围内的成员模板的显式特化声明中,成员模板和它的一些封闭类模板可能保持未特化,除非声明不应显式特化类成员模板,如果它封闭类模板也没有明确专门化。

因为:

  1. 显式特化的函数模板、类模板或变量模板的声明应在显式特化的声明之前。

因此这不应该在外部模板声明中:

template <typename U>
struct A {
  template<typename...Ts> auto func();
  template<> auto func<int>() { return std::string{"foo"}; } // should not work
};

我不知道为什么clang允许这样做。

但它允许在声明主模板的命名空间范围内,在本例中为全局命名空间范围。


推荐阅读