我有以下问题:
假设有一个基类A
。
这个类有很大一部分总是一样的。一小部分应该是通用的,例如使用模板参数T
。
现在我不想创建A
的模板类,因为我不希望在头文件中有大的非泛型部分实现。我该怎么办?有没有可能我从类头中分离出大的非泛型部分的实现,但也有一些小的通用部分作为某种模板?
作为示例:假设您有一个通用树,它有一些模板函数(如add(T o)
),但许多函数不依赖于模板(如size()
,但更复杂)。
作为补充:A
有多个子类,那些应该决定最终T
的类型...
我知道编译器无法处理cpp文件中模板的实现。但是,有没有一种概念性的解决方法?例如,不同于模板的东西?
答案 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 ++替代模板,那么它们就不会提供模板所具有的强大功能。除此之外,让我们试着解决你的问题。
您可以简单地将函数打包到一个单独的类中,您可以在任何其他标题中实现它。我们在这里有两个选择,将代理类作为成员添加到基类或使用继承。
使用此方法遇到问题,他们无法直接访问这些功能。您无法使.
运算符超载!处理此问题的一种方法是代理函数,它只返回对成员变量的引用。这是一个最小的工作示例:
#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;
}
使用这种方法,您将能够直接访问这些功能,但这是以使用继承为代价的,我知道有些人不会选择使用继承。这是一个有效的例子:
#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,我不想让客户端需要显式调用函数,只是调用另一个函数。你显然可以使用一些涉及typedef
和bind
的技巧,但我认为这会增加不必要的开销。