4

IMO,C++ 模板规则似乎过于严格,并且定义了编译器实现。但是在这里,我有一个特定的行为,我很难理解。

在下面的问题中,我有意识地尽量避免明确地专门化父类。

问题是,我可以部分专门化一个成员,但不能完全专门化它。这确实违反直觉,因为您可以轻松地将虚拟模板添加到完全专业化的模板并使其部分专业化。这里发生了什么??

重要编辑:这很重要,因为我们知道,如果不专门化类,就不能专门化成员函数(这可以看作是这个问题的组合,需要部分专门化,而 c++ 没有允许部分专门化的函数。我不知道这些是否相关,但至少它们是一致的),因此,如果你想在你的类中使用一个你可以专门化的函数,你就会被使用函子困住。最重要的是,您需要添加一个虚拟模板参数才能使其正常工作!

这有效:

template <class T>
class A
{
  template<typename Y, typename Z>
  struct C{
      void operator()(int x);
  };

  template<typename Z>
  struct C<int, Z>{
      void operator()(int x);
  };
};

template <class T>
template <typename Z>
void A<T>::C<int, Z>::operator()(int x){

}

但这不会:

template <class T>
class A
{
  template<typename Y>
  struct C{
      void operator()(int x);
  };

  template<>
  struct C<int>{
      void operator()(int x);
  };
};

template <class T>
template <>
void A<T>::C<int>::operator()(int x){

}

编辑:Sean F. 在评论中指出编译器很难选择专业化。但这里的问题是,部分专业化并不能解决这个问题。所以这不可能是答案。

4

1 回答 1

2

作为一般规则,模板函数专业化是一个坏主意。使用重载或标签调度。例如:

template<class T>struct tag_t{};

template<class T>
auto foo(T& t){ return foo( tag_t<T>{}, t ); }

现在我们可以使用重载来调度:

void foo( tag_t<int>, int& i ){ i+=3; }
template<class T>
void foo( tag_t<T>, T& t ){ t*=2; }

这里没有专业化。我们使用重载解析规则来选择实现。这适用于成员。

我们只在顶级课程上使用专业化。即使在那里,我也经常想通过标记调度和使用 decltype 来选择实现,因为重载解决方案通常会提供更好的模式匹配。

如果您确实需要成员类,请将您的成员类替换为顶级类和一些朋友声明。

template<class T, class Y>
struct A_C;
template <class T>
class A
{
  template<class T0, class Y0>
  friend class A_C<T0, Y0>;
  template<class Y>
  using C=A_C<T,Y>;
};
template<class T, class Y>
struct A_C {
  // implementation of A::C
};

现在你在A_C这里有了非常不受限制的专业化。没有你的问题。

于 2018-06-07T02:35:37.513 回答