关于c ++:模板类的工厂模式和模板类的新类型的默认参数

Factory Pattern for Template Classes and Default Argument of New Type for Template Classes

我有三层类,例如BaseSpreading(基类)、IterativeMapSpreading(BaseSpreading的一个子类)和TentSpreading、BernoullisSpreading(IterativeMapSpreading的两个子类)。一个名为GenerateSpreading的方法出现在所有类中。用户定义的选项指定分布,例如"帐篷"或"伯努利"。然后,if-else控制结构调用相应子类的generatespreading方法("帐篷"和"bernoulli"将分别调用tentspreading和bernoullispreading子类的generatespreading方法)。

我知道如何通过将generatespreading声明为virtual来解决我的问题,并定义一个基类指针(指向basespreading)。然后,可以根据用户选项在if-else控制结构内实例化派生类对象。尽管这样的实例在控制结构的作用域之外不可用,但是可以使用工厂模式将基类指针指向派生类对象,请参见本篇文章的示例。虽然基于虚拟方法的解决方案可以工作,但是由于它的性能不好,因此对于我的目的来说并没有用处。我的模拟调用了无数次虚拟函数。

作为一种替代方法,我使用模板编程。在这种情况下,我必须处理嵌套模板类的实例在if-else结构的范围之外不可用的问题。我的问题是,在使用模板类时是否可以扩展工厂模式思想。为了实现这一点,在可能的情况下,我必须创建一个指向模板基类的指针,然后将其指向实例化的模板子类,该子类在if-else结构中定义。然后问题归根结底就是为模板基类定义一个默认的模板参数,这一直是我的难题。此链接提供了一个示例,说明如何定义一个不是众所周知类型的默认模板参数,如"int"(链接示例的默认参数是向量的标准"分配器"模板)。

以下是我的脚本:

基底扩展.h:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
#ifndef BASE_SPREADING_H_
#define BASE_SPREADING_H_

#include <boost/filesystem.hpp>
#include <boost/filesystem/fstream.hpp>
#include <boost/numeric/ublas/vector.hpp>

template <class S>
class BaseSpreading
{
  public:
    BaseSpreading(S& spreading);

    void generateSpreading(boost::numeric::ublas::vector<double>&);
  private:
    S& spreading_;
};

template <class S>
BaseSpreading<S>::BaseSpreading(S& spreading) : spreading_(spreading) {}

template <class S>
void BaseSpreading<S>::generateSpreading(
  boost::numeric::ublas::vector<double>& spr) {
  spreading_.generateSpreading(spr);
}

#endif /* BASE_SPREADING_H_ */

展开迭代图。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
#ifndef SPREADING_ITERATIVE_MAP_H_
#define SPREADING_ITERATIVE_MAP_H_

#include <boost/numeric/ublas/vector.hpp>

template <class S>
class IterativeMapSpreading
{
  public:
    IterativeMapSpreading(S& spreading);

    double evaluateMap(double);
    void generateSpreading(boost::numeric::ublas::vector<double>&);
    double sampleInitialPoint();
  private:
    S& spreading_;

    void calculateFollowingPoints(boost::numeric::ublas::vector<double>&);
};

template <class S>
IterativeMapSpreading<S>::IterativeMapSpreading(S& spreading) :
  spreading_(spreading) {}

template <class S>
void IterativeMapSpreading<S>::calculateFollowingPoints(
  boost::numeric::ublas::vector<double>& spr) {
    for (unsigned int i=1; i<spr.size(); ++i) {
      spr(i) = spreading_.evaluateMap(spr(i-1));
    }
}

template <class S>
double IterativeMapSpreading<S>::evaluateMap(double x) {
  return spreading_.evaluateMap(x);
}

template <class S>
void IterativeMapSpreading<S>::generateSpreading(
  boost::numeric::ublas::vector<double>& spr) {
  spr(0) = spreading_.sampleInitialPoint();
  calculateFollowingPoints(spr);
}

template <class S>
double IterativeMapSpreading<S>::sampleInitialPoint() {
  return spreading_.sampleInitialPoint();
}

#endif /* SPREADING_ITERATIVE_MAP_H_ */

展开图H:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
#ifndef SPREADING_TENT_H_
#define SPREADING_TENT_H_

#include <math.h>

#include"random_number_generation.h"

class TentSpreading
{
  public:
    TentSpreading(double uniformMin=0, double uniformMax=1,
      double nonCentrality=0.5);

    double evaluateMap(double);
    double sampleInitialPoint();
  private:
    const double uniformMin_, uniformMax_, nonCentrality_;
    double leftIntercept_, leftSlope_, rightIntercept_, rightSlope_;
    boost::random::uniform_real_distribution<> Uniform;

    void setLines();
    void validateParameters() const;
};

#endif /* SPREADING_TENT_H_ */

摊铺CPP:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
#include"spreading_tent.h"

TentSpreading::TentSpreading(double uniformMin, double uniformMax,
  double nonCentrality) : uniformMin_(uniformMin), uniformMax_(uniformMax),
  nonCentrality_(nonCentrality), Uniform(uniformMin, uniformMax) {
  setLines();
}

double TentSpreading::evaluateMap(double x) {
  double y;

  if((uniformMin_<=x) && (x<nonCentrality_))
    y = leftSlope_*x+leftIntercept_;
  else if((nonCentrality_<=x) && (x<=uniformMax_))
    y = rightSlope_*x+rightIntercept_;

  return y;
}

double TentSpreading::sampleInitialPoint() {
  return Uniform(rng);
}

void TentSpreading::setLines() {
  leftSlope_ = (uniformMax_-uniformMin_)/(nonCentrality_-uniformMin_);
  leftIntercept_ = -uniformMin_*(uniformMax_-nonCentrality_)/
    (nonCentrality_-uniformMin_);
  rightSlope_ = -(uniformMax_-uniformMin_)/(uniformMax_-nonCentrality_);
  rightIntercept_ = (pow(uniformMax_, 2)-uniformMin_*nonCentrality_)/
    (uniformMax_-nonCentrality_);
}

最后,main.cpp中感兴趣的部分:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
  try  {
    if (sbcOptions.mode=="sim-spr") {
      boost::numeric::ublas::vector<double> sprVector(3);

      if (sbcOptions.spr=="tent") {
        TentSpreading tent(-1, 1, 0);
        IterativeMapSpreading<TentSpreading> map(tent);
        BaseSpreading<IterativeMapSpreading<TentSpreading> > spreading(map);
      }

      spreading.generateSpreading(sprVector);
    }
  }
  catch(std::logic_error& logicError) {
    logTee << logicError.what() <<"
"
;
    return 1;
  }

为了使用工厂模式,我尝试添加一个抽象的基类,称为FactoryBaseSpreading,其中BaseSpreading是一个子类。这就是工厂基础扩展的定义:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
FactoryBaseSpreading {
  public:
    static BaseSpreading* create(std::string type);
}

FactoryBaseSpreading* FactoryBaseSpreading::create(std::string type) {
  if (type =="tent") {
    TentSpreading tent(-1, 1, 0);
    IterativeMapSpreading<TentSpreading> map(tent);
    return new BaseSpreading<IterativeMapSpreading<TentSpreading> >(map);
  }
  return NULL;
}

//Also, change the following line in base_spreading.h:
class BaseSpreading : public FactoryBaseSpreading

这样,我就可以在"main.cpp"中定义一个指向FactoryBaseSpreading的指针,并将其指向if-else结构内的适当子类,例如:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
FactoryBaseSpreading* spreading;

try  {
  if (sbcOptions.mode=="sim-spr") {
    boost::numeric::ublas::vector<double> sprVector(3);

    if (sbcOptions.spr=="tent") {
      spreading = FactoryBaseSpreading::create("tent");
    }

    spreading.generateSpreading(sprVector);
  }
}
catch(std::logic_error& logicError) {
  logTee << logicError.what() <<"
"
;
  return 1;
}

如果我在FactoryBaseSpreading中定义了一个虚拟的GenerateSpreading方法,那么它编译并运行正常。然而,我试图避免的是使用虚拟功能,所以工厂模式并不能解决我的问题。

因此,我考虑在BaseSpreading基类中定义"create"方法,而不创建FactoryBaseSpreading抽象基类。在这种情况下,"创建"方法的定义为:

1
2
3
4
5
6
7
8
9
template <class S>
BaseSpreading<S>* BaseSpreading<S>::create(std::string type) {
  if (type =="tent") {
    TentSpreading tent(-1, 1, 0);
    IterativeMapSpreading<TentSpreading> map(tent);
    return new BaseSpreading<IterativeMapSpreading<TentSpreading> >(map);
  }
  return NULL;
}

此代码的问题是,我无法再定义基类指针,因为它依赖于模板参数s。例如,这是无效的:

1
BaseSpreading<S>* spreading;

这就是为什么我要问我是否可以在basespreading的定义中提供一个默认参数。即使我能够指定默认的模板参数,我也无法在运行时切换模板参数,因为模板是由编译器在编译时实例化的,如本文所述。所以,我似乎不能避免使用虚拟函数,即使在模板元编程的帮助下也是如此。我唯一能想到的解决这个问题的方法是通过函数指针,委托函数,或者使用函数编程完全改变代码的设计…


如果我正确理解了所有这些,那么您需要基于您的内部循环中不会改变的内容进行动态调度,并且您希望内部循环不包含任何"if"语句或间接调度。这可以通过对代码进行少量的重组来实现:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
template<typename SPREADING>
void inner_loop(SPREADING* spreading) {
  while(something_long) {
    spreading->whatever();
  }
}

main(){
  if (opts.tentspreading) {
    TentSpreading spreading(stuff);
    inner_loop(&spreading);
  } else {
    IterativeMapSpreading spreading(other,stuff);
    inner_loop(&spreading);
  }
}

请注意,不能统一两个inner_loop(&;spreading)语句。实际上,它们使用不同的参数调用不同的函数。