【问题标题】:Template specialization by another template (of same class)另一个模板(同一类)的模板特化
【发布时间】:2011-09-01 10:14:13
【问题描述】:

我正在编写一个数组类。这个数组类可以再次包含数组作为成员。在实现打印功能时,我需要专业化。

26:template <class T> class array : public vector<T>{
public:
    ...
       string* printToString();
    ...
};
...           
template <class T> string* array<T>::printToString(){
   ...  // generic function
}
template <> inline string* array<double>::printToString(){
   ...  // spezialization for double, works
}
561:template <class U> string* array<array<U>*>::printToString(){
   ...  // does not work
}

最后一个定义产生

src/core/array.h:561: error: invalid use of incomplete type ‘class array<array<T> >’
src/core/array.h:26: error: declaration of ‘class array<array<T> >’

如果重要的话,g++ 版本是 g++ (Ubuntu 4.4.3-4ubuntu5) 4.4.3。 有什么想法有什么问题吗?

提前致谢, 托马斯

【问题讨论】:

  • 你不应该从标准库容器派生,它们不是为了派生而来的。
  • 你真的返回 pointersstd::string 吗?
  • 另外,array&lt;array&lt;U&gt;*&gt; 不是数组数组。它是一个指针数组。你真的应该摆脱那个* 键:+

标签: c++ templates template-specialization specialization


【解决方案1】:

作为大卫解决方案的替代方案,您可以无条件地将调用转发给一组重载函数:

template <class T> class array;
namespace details {
  template <class T> std::string array_print(array<T> const&);
  std::string array_print(array<double> const&); // Regular function 
  template <class T> std::string array_print(array<array<T> > const&);
}

template <class T> class array : private vector<T> {
public:
    ...
       std::string printToString() { return details::array_print(*this); }
    ...
};

namespace details { /* implementions after class is defined */ }

【讨论】:

  • +1,提供非模板化重载(几乎)总是比专门化更好。
  • 是的,这似乎更简单。 @David,@MSalters 但是,在完全专业化的形式中,它不允许我使用参数,即string array_print( array&lt;double&gt;&amp; ar ) {size_type len=ar.size()} 将给出src/core/array.h:59: error: invalid use of incomplete type ‘struct array&lt;double&gt;’。声明是template &lt;class T&gt; class array;
  • 是的,在这里定义和声明的顺序有点快和松散。我已将定义向下移动。或者,您可以在类定义之后移动namespace details,并在其下方移动array&lt;T&gt;::printToString() 的实现。
【解决方案2】:

你不能部分特化一个函数,你只能完全特化它,这就是为什么你可以为double提供特化,但不能为array&lt;U&gt;提供特化,其中U是泛型类型。

您可以通过使用类模板来绕过这个限制,并对其进行部分特化,但这会有点麻烦。

namespace detail {
   template <typename T>
   struct array_printer {
      static std::string print( array<T> const & array ) {
         // basic implementation
      }
   };
   template <typename T>
   struct array_printer< array<T> > {
      static std::string print( array< array<T> > const & array ) {
         // specialization for array<T>
      }
   }
}

然后将成员函数实现为对适当重载的简单分派:

template <typename T> 
class array : std::vector<T> {  // do not publicly derive from STL containers
public:
   std::string printToString() const {
      return detail::array_printer<T>::print( *this );
   }
}

当然,实际代码中的情况要复杂一些,您必须适当地对代码进行排序,并提供模板的前向声明等等,但这应该足以让您入门。

【讨论】:

    【解决方案3】:

    您的函数必须完全专业化。例如:

    // Fully specialized. You cannot replace `double` with generic parameter.
    template <>
    string* array<array<double>*>::printToString(){
        return nullptr;
    }
    

    但是,您的课程可以是部分专业化的。例如:

    template <class T> class array : public vector<T>{
    public:
        string* printToString();
    };
    
    template <class T> string* array<T>::printToString(){
        return nullptr;
    };
    
    // Partial specialization.
    template <class T> class array<array<T>*> : public vector<T>{
    public:
        string* printToString();
    };
    
    template <class T> string* array<array<T>*>::printToString(){
        return nullptr;
    };
    

    -- 编辑 ---

    泛型类的方法不会被类特化自动“采用”,反之亦然。但是,您可以使用继承来“自动”重用泛型类中的方法。比如……

    template <class T> class array : public vector<T>{
    public:
        string* printToString();
        void f();
    };
    
    // (1a)
    template <class T> string* array<T>::printToString(){
        return nullptr;
    };
    
    // (2)
    template <class T> void array<T>::f(){
    };
    
    template <class T> class array<array<T>*> : public array<T> {
    public:
        string* printToString();
    };
    
    // (1b)
    template <class T> string* array<array<T>*>::printToString(){
        return nullptr;
    };
    
    void Test() {
    
        array<double> a1;
        a1.printToString(); // Calls (1a).
        a1.f(); // Calls (2).
    
        array<array<char>*> a2;
        a2.printToString(); // Calls (1b).
        a2.f(); // Calls (2).
    
    }
    

    ...这可能是也可能不是您需要的(可能需要一些“手动”重复)。

    【讨论】:

    • 谢谢,但是部分特化的类是否需要再次声明和定义所有方法,还是会从通用模板中获取缺少的方法?
    • @Thomas 一些有限的重用可能是可能的 - 请参阅编辑后的答案。
    • 谢谢!哇,专业化现在是其自身泛化的子类。他们在构建编译器时真的考虑到了这一点吗?
    • @Branko:这种继承关系的问题在于你借用了不同的方法。给定一个函数f,它将int 作为原始模板中的参数:type array&lt; int &gt;::f( int ),带有array&lt;int&gt; 的特化将具有不同的签名:type array&lt; array&lt;int&gt; &gt;::f( int )(请注意,array&lt;&gt; 之一已被删除! )
    • @David True (+1)。正如我所说,这“可能是也可能不是你需要的”。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2020-11-15
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多