如何使用可变模板的CRTP
How to use CRTP with variadic templates?
假设我最初使用CRTP有以下设计:
template<class Outputter> class Generator {
protected:
vector<int> v;
private:
void work(ostream& out) {
// perform first part of some complex operations on v
out << *static_cast<Outputter *>(this);
// perform second part of some complex operations on v
out << *static_cast<Outputter *>(this);
// many more ....
// perform some final actions
}
public:
Generator(unsigned length): v(length) {}
friend ostream& operator<<(ostream& out, Outputter&& generator) {
// perform some preparation work
work(out);
// perform some final actions
return out;
}
};
class SimpleDumpOutputter : public Generator<SimpleDumpOutputter> {
private:
unsigned count;
public:
SimpleDumpOutputter(unsigned length): Generator(length), count() {}
friend ostream& operator<<(ostream& out, SimpleDumpOutputter& outputter) {
out << "Step " << ++count << " of calculation: "
copy(outputter.v.begin(), outputter.v.end(), ostream_iterator<int>(out, " "));
out << endl;
return out;
}
};
class FancyOutputter : public Generator<FancyOutputter> { // create a graph using graphviz's dot language to visualise v
private:
// abbreviated
public:
FancyOutputter(unsigned length): Generator(length) {}
friend ostream& operator<<(ostream& out, FancyOutputter& outputter) {
// write statements to out
return out;
}
};
// some more different Outputters, for example an Outputter that creates a pretty LaTeX document
在本设计中,有一个Generator
CRTP类模板,它在vector<int> v
上执行复杂的计算,并在计算的每一步/部分使用其派生类的好友operator<<
打印结果。
这是我想实现的一个有趣的概念:我想在一次执行中以多种格式输出。具体来说,我认为我可以这样做:
template<class Outputters> class AggregateOutputter : public Generator<AggregateOutputter<Outputters...> > {
private:
static const unsigned outputter_count = sizeof...(Outputters);
typedef array<ostream *, outputter_count> DestArr;
DestArr destinations;
public:
AggregateOutputter(unsigned v_length, DestArr destinations): IsomerGenerator<AggregateOutputter<Outputters...> >(length), destinations(destinations) {}
friend ostream& operator<<(ostream&, AggregateOutputter& outputter); // first argument is dummy, because we would use the ostreams in destinations
}
这个想法是用户将使用,比如说,AggregateOutputter<SimpleDumpOutputter, FancyOutputter
,并使用两个ostream
中的array
来构造对象。每当Generator
在输出类上调用operator<<
时,AggregateOutputter
将遍历destinations
中的ostream
s和Outputters
中的类型,并调用类似*dest_iter << *static_cast<Outputter_Iter>(this);
的内容。
我不确定这将如何工作,虽然。我不确定多重继承是否可以这样使用,是否有可能在array
和参数化类型包之间"压缩"。有人了解这种情况吗?
我修改了你原来的设计。我认为生成器在调用输出操作符时进行大量计算至少可以说是令人惊讶的。也可以让AggregateOutputter输出忽略<<的ostream参数也令人惊讶。此外,Outputter与Generator没有is-a关系。
我试着把关注点分开,最后没有使用CRTP,而是使用可变模板,但我认为它做了你想要的。
http://ideone.com/xQrnW4#include <vector>
#include <iostream>
#include <iterator>
#include <array>
using namespace std;
class Generator {
protected:
vector<int> v;
public:
Generator(unsigned length): v(length) {}
template<class Outputter>
void do_calculations_with_output(Outputter& out){
// perform first part of some complex operations on v
out.output(v);
// perform second part of some complex operations on v
out.output(v);
// perform some final actions
}
};
class SimpleDumpOutputter {
private:
ostream* out;
unsigned count;
public:
SimpleDumpOutputter(ostream& os): out(&os), count() {}
template<class C>
void output(const C& c) {
*out << "Step " << ++count << " of calculation: ";
copy(c.begin(),c.end(), ostream_iterator<int>(*out, " "));
*out << endl;
}
};
class FancyOutputter {
ostream* out;
int count;
public:
FancyOutputter(ostream& os): out(&os),count() {}
template<class C>
void output(const C& c) {
// create a graph using graphviz's dot language to ease visualisation of v
*out << "Step " << ++count << " of calculation: ";
*out << "Graphviz outputn";
}
};
template<class... Outputters> class AggregateOutputter : private Outputters... {
private:
template<class First, class... Rest>
struct output_helper{
template<class C>
static void do_output(AggregateOutputter* pthis,const C& c){
static_cast<First*>(pthis)->output(c);
output_helper<Rest...>::do_output(pthis,c);
}
};
template<class First>
struct output_helper<First>{
template<class C>
static void do_output(AggregateOutputter* pthis,const C& c){
static_cast<First*>(pthis)->output(c);
}
};
public:
template<class... Out>
AggregateOutputter( Out&... out): Outputters(out)...{}
template<class C>
void output(const C& c) {
output_helper<Outputters...>::do_output(this,c);
}
};
int main(){
AggregateOutputter<FancyOutputter,SimpleDumpOutputter> out(cout,cout);
Generator g(10);
g.do_calculations_with_output(out);
}
好的,这是我想到的一个解决方案,受到约翰·班德拉的启发。(关于为什么我认为他的方法不符合我的需求,请参阅我对答案的评论)
template<class... Outputters> class AggregateOutputter : public Generator<AggregateOutputter<Outputters...> > {
private:
typedef array<ostream *, sizeof...(Outputters)> DestArr;
DestArr destinations;
typedef typename DestArr::iterator DestArrIter;
struct OutputterHolder : public Outputters... {
OutputterHolder(vector<int>& v): Outputters(v)... {}
} outputter_holder;
template<class First, class... Rest> struct OutputHelper {
static void do_output(OutputterHolder *pthis, DestArrIter dest) {
**dest << *static_cast<First *>(pthis);
OutputHelper<Rest...>::do_output(pthis, ++dest);
}
};
template<class First> struct OutputHelper<First> {
static void do_output(OutputterHolder *pthis, DestArrIter dest) {
**dest << *static_cast<First *>(pthis);
}
};
public:
template<typename... OstreamStar> AggregateOutputter(unsigned length, OstreamStar... ostreams): Generator<AggregateOutputter<Outputters...> >(length), destinations{{ostreams...}}, outputter_holder(this->v) {
static_assert(sizeof...(OstreamStar) == sizeof...(Outputters), "number of outputters and destinations do not match");
}
friend ostream& operator<<(ostream& dummy_out, AggregateOutputter& outputter) {
OutputHelper<Outputters...>::do_output(&outputter.outputter_holder, outputter.destinations.begin());
// possibly write some logging info to dummy_out
return dummy_out;
}
};
// to use this:
ofstream fout("gv.gv");
cout << AggregateOutputter<FancyOutputter, SimpleDumpOutputter>(length, &fout, &cout);
这个想法是,除了约翰的答案中的output_helper
(我已将其重命名为OutputHelper
)之外,还有另一个辅助struct
称为OutputterHolder
,它继承自所有Outputters
。我还使用了ostream *
的array
来存储输出的目的地,并修改了do_output
,使其也采用iterator
,以便可以匹配正确的ostream
。
重要的是,为了配合更改,我已将Generator
中的受保护成员vector<int> v
更改为引用,即vector<int>& v
,以便outputter_holder
中的数据结构可以改为引用AggregateOutputter
中的结构。这还需要在所有接受vector<int>&
参数的输出器中添加另一个构造函数。原来的构造函数使用v
的长度,现在将使用new
分配内存。
我不确定我想出的这个解决方案是最好的/最优雅的解决方案。
- 尝试使用继承和模板实现CRTP.Visual Studio正在生成编译器错误
- 检测使用 CRTP 的类中的成员函数
- 简化使用 CRTP 模式的类的声明
- 概念可以与 CRTP 习语一起使用吗?
- 在 CRTP 中使用嵌套名称说明符
- 使用 CRTP 实现单例
- c++ 使用 CRTP 为变量模板中的每个类型创建纯虚拟重载
- 是否可以将CRTP与本身已模板化的派生类一起使用
- 使用CRTP将基类的子类传递给构造函数
- 如何使用 CRTP 创建少量对象?
- 使用 CRTP 进行选择性静态成员继承
- 将 CRTP 与部分类专用化结合使用?
- C++使用 CRTP 模板模板基类中的构造函数
- 在运行时选择要使用的 CRTP 实现
- 如何使用 CRTP 创建具有基类的可选模板参数
- 如何使用 CRTP 自注册类实例?
- 我可以将CRTP与虚拟函数或函数一起使用,以供访问者算法更改的访问者算法
- SFINAE和CRTP使用clang++和g++的一些魔力
- 使用CRTP确定生成代码
- 在CRTP中使用模板参数的嵌套类