问题描述
假设我们最初使用CRTP进行以下设计:
Let's suppose originally I have the following design using 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<<
来打印计算的每个步骤/部分的结果。
In this design, there is a Generator
CRTP class template that performs complex calculations on the vector<int> v
and prints the result at each step/part of calculation using its derived classes's befriended operator<<
.
这是我要实现的一个有趣的概念:我希望在一次执行中以多种格式输出。具体来说,我想我可以做到:
Here's an interesting concept that I want to implement: I would want outputs in multiple formats in a single execution. Specifically, I thought I could do:
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
并使用 array
构造两个对象 ostream
s。每当 Generator
在输出程序类上调用 operator <<<
时, AggregateOutputter
将迭代目的地
中的 ostream
s和 Outputters code>并沿
* dest_iter<< * static_cast< Outputter_Iter>(this);
。
The idea is that the user would use, say, AggregateOutputter<SimpleDumpOutputter, FancyOutputter
and construct the object with an array
of two ostream
s. Whenever Generator
calls operator<<
on the outputter class, the AggregateOutputter
will iterate through the ostream
s in destinations
and the types in Outputters
and invoke something along the lines of *dest_iter << *static_cast<Outputter_Iter>(this);
.
我不确定这如何工作。我不确定是否可以通过这种方式使用多重继承,也不确定是否可以在数组
和一堆参数化类型之间压缩。
I'm not sure how this would work though. I'm not sure whether multiple inheritance can be used this way, whether it is possible to "zip" between an array
and a pack of parameterised types. Is anyone knowledgable in this situation?
推荐答案
我修改了您的原始设计。我认为,至少可以说,在调用输出运算符时Generator进行了大量的计算。还要让您的AggregateOutputter输出以忽略<<的ostream参数。也令人惊讶。另外,Outputter与Generator不存在is-a关系。
I modified your original design. I thought Generator doing a bunch of calculations when the output operator is called is surprising to say the least. Also for your AggregateOutputter to output to ignore the ostream parameter of << is also surprising. Also, Outputter does not have an is-a relationship with Generator.
我试图找出问题所在,最终没有使用CRTP而是使用可变参数模板,但是我认为它可以满足您的要求。
I tried to separate out the concerns, and ended up not using CRTP but using variadic templates, but I think it does what you want.
#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 output\n";
}
};
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);
}
这篇关于如何将CRTP与可变参数模板一起使用?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持!