模板类中

时间:2016-04-12 15:36:12

标签: c++ templates

我有以下问题:

假设有一个基类A

这个类有很大一部分总是一样的。一小部分应该是通用的,例如使用模板参数T

现在我不想创建A的模板类,因为我不希望在头文件中有大的非泛型部分实现。我该怎么办?有没有可能我从类头中分离出大的非泛型部分的实现,但也有一些小的通用部分作为某种模板?

作为示例:假设您有一个通用树,它有一些模板函数(如add(T o)),但许多函数不依赖于模板(如size(),但更复杂)。

作为补充:A有多个子类,那些应该决定最终T的类型...

我知道编译器无法处理cpp文件中模板的实现。但是,有没有一种概念性的解决方法?例如,不同于模板的东西?

2 个答案:

答案 0 :(得分:0)

直接处理分离的一种方法是将模板拆分为两个头文件(这非常类似于将非模板内容分成.h声明和.cpp实现的方式)。

然后,您可以在模板上使用显式模板实例化。

  // tree.h
  // Included by anything that needs to use the tree template.
  template <typename T> class tree {
     void inline_func() { }
     void noninline_func();
  };

  // tree_impl.h
  // This header is only included by a single .cpp file: the
  // template repository module that contains instantiations.
  template <typename T> tree<T>::noninline_func()
  {
  }

  // template-repo.cpp
  #include "tree.h"
  #include "tree_impl.h" // only template-repo.cpp includes this!
  // same for all other templates
  #include "mytype.h"
  #include "othertype.h"

  template class tree<mytype>; // instantiate template here over mytype
  template class tree<othertype>; // ditto over othertype
  // ... other instantiations

您可以使用预处理器将两个头文件一起滚动:

  // tree.h
  template <typename T> class tree {
     void inline_func() { }
     void noninline_func();
  };

  #ifdef INSTANTIATING_TEMPLATES
  template <typename T> tree<T>::noninline_func()
  {
  }
  #endif

  // template-repo-cpp
  #define INSTANTIATING_TEMPLATES
  #include "tree.h"
  #include "mytype.h"

  template class tree<mytype>;

然而,这种轻微的便利意味着当你#include "tree.h"到处时,你会通过预处理器传递大量的额外信息。

所有这些都有一个缺点,就像C ++一样。它不仅是手动维护,而且每次触摸模板标题或用作模板参数的类型之一时,都必须重新编译整个template-repo.cpp。 (当然,事情可以分成多个小型回购:针对变通方法问题的反制方法)。

答案 1 :(得分:0)

  

但是,是否有解决分离的概念方法?例如   与模板不同的东西?

如果您正在寻找一种可移植的c ++替代模板,那么它们就不会提供模板所具有的强大功能。除此之外,让我们试着解决你的问题。

您可以简单地将函数打包到一个单独的类中,您可以在任何其他标题中实现它。我们在这里有两个选择,将代理类作为成员添加到基类或使用继承。

选择A(直接包括作为成员)

使用此方法遇到问题,他们无法直接访问这些功能。您无法使.运算符超载!处理此问题的一种方法是代理函数,它只返回对成员变量的引用。这是一个最小的工作示例:

#include <iostream>
using namespace std;

// forward declaration 
template <typename T> 
class B;

template <typename T>
class A {
public:

    // allows friendship regardless of the type instantiated with class A
    template <typename U>
    friend class B;

    A(int a) : a_(a), funcs_(this) {}

    // this could be named anything
    B<T>& utils() { return funcs_; }

private:
    int a_;
    // proxy class instance, should probably be static 
    B<T> funcs_;
};

// this class could be modifed to accept any type but i did this for simplicity 
template <typename T> 
class B {
public:

    explicit B(A<T>* base) : base_(base) {}

    // function implementations go here...
    void print() { std::cout << base_->a_; }

private:
    A<T>* base_;
};


int main() {

    A<int> testA(23);

    testA.utils().print();

    return 0;
}

选择B(继承)

使用这种方法,您将能够直接访问这些功能,但这是以使用继承为代价的,我知道有些人不会选择使用继承。这是一个有效的例子:

#include <iostream>
using namespace std;

// forward declaration 
template <typename T> 
class B;

template <typename T>
class A : public B<T> {
public:

    // allows friendship regardless of the type instantiated with class A
    template <typename U>
    friend class B;

    A(int a) : a_(a), B(this) {}

private:
    int a_;

};

// this class could be modifed to accept any type but i did this for simplicity 
template <typename T> 
class B {
public:

    explicit B(A<T>* base) : base_(base) {}

    // function implementations go here...
    void print() { std::cout << base_->a_; }

private:
    // pointer to base class 
    A<T>* base_; 
};


int main() {

    A<int> testA(23);

    testA.print();

    return 0;
}

如果是我的选择,我很可能会使用选择B,我不想让客户端需要显式调用函数,只是调用另一个函数。你显然可以使用一些涉及typedefbind的技巧,但我认为这会增加不必要的开销。