4

我正在使用 boost typelist 以下列方式实现策略模式。

using namespace boost::mpl;

template <typename PolicyTypeList = boost::mpl::vector<> >
class Host : public inherit_linearly<PolicyTypeList, inherit<_1, _2> >::type
{
public:
    Host() : m_expensiveType(/* ... */) { }

private:
    const ExpensiveType m_expensiveType;
};

该类Host知道如何创建 的实例ExpensiveType,这是一项代价高昂的操作,并且每个策略类都公开了使用它的功能。策略类将始终至少具有以下示例策略中定义的构造函数。

struct SamplePolicy
{
    SamplePolicy(const ExpensiveType& expensiveType)
        : m_expensiveType(expensiveType) { }

    void DoSomething()
    {
        m_expensiveType.f();
        // ...
    }

private:
    const ExpensiveType& m_expensiveType;
};

是否可以Host以调用每个给定策略的构造函数的方式定义构造函数?如果不涉及类型列表,这很容易,因为每个策略的类型都是明确已知的。

template <typename PolicyA, typename PolicyB>
class Host : public PolicyA, public PolicyB
{
public:
    Host() :
        m_expensiveType(/* ... */),
        PolicyA(m_expensiveType),
        PolicyB(m_expensiveType) { }

private:
    const ExpensiveType m_expensiveType;
};

boost::mpl::for_each算法看起来很有希望,但我不知道如何使用它来解决这个问题。

4

4 回答 4

4

如果你想要这样的一代,我只能推荐阅读 Alexandrescu 的Modern C++ Design。有一整章专门用于从类型列表生成层次结构。你也可以在 Loki 的网站上找到它:Hierarchy Generators;虽然你会错过图表和解释,以及过程。

对于您的特定问题,这似乎很简单。

// Helper
struct nil
{
};

template < class Head, class Tail = nil>
struct SH: Head<Tail> /* for SimpleHierarchy */
{
  SH(const ExpensiveType& e): Head(e), SH<Tail>(e) {}
};

template<>
struct SH<nil,nil>
{
  SH(const ExpensiveType& e) {}
}:

// Policies
class A
{
public:
  A(const ExpensiveType& e) : T(e), m_e(e) {}

private:
  const ExpensiveType& m_e;
};

class B
{
public:
  B(const ExpensiveType& e) : T(e), m_e(e) {}

private:
  const ExpensiveType& m_e;
};

class C
{
public:
  C(const ExpensiveType& e) : T(e), m_e(e) {}

private:
  const ExpensiveType& m_e;
};

// Use
// nesting example
typedef SH<A, SH<B,C> > SimpleHierarchy;

// Your example, revisited
template <class A, class B>
class Host: SH<A,B>
{
public:
  Host(const ExpensiveType& e): SH<A,B>(e), m_e(e) {}

private:
  const ExpensiveType& m_e;
};

当然,这只是草图。这里的主要问题是可扩展性。如果你读过 Alexandrescu 的书,你会学到更多,如果你没有时间,那就去看看源代码吧,这可能正是你所需要的。

有很多方法可以直接从 中做mpl::vector,唯一需要意识到的是,你不能用一个大的 MI 单层来做到这一点,但你可以添加很多层。

在这里,我选择不在策略级别增加复杂性(它们没有模板化),而是在每个级别依赖 MI(双重)。你可以让它纯粹是线性的,但是模板化你的策略意味着你不能在源文件中定义它们。

另请注意,这种方法可以mpl::vector直接采用,但这将涉及使用元模板编程操作:back,至少,这可能会使代码混淆,而不是实际帮助pop_backempty

于 2009-11-04T08:14:36.233 回答
3

如评论中所述,您需要链接构造函数调用。为此,派生链中的每个类型都必须知道它是从什么类型派生的——为了允许任意派生序列,我们需要制作该类型的模板,以便它们的基可以是任何类型。
这允许我们引用基础并显式调用其构造函数。

我设计了一个基本示例,但最终没有使用 boost,因为mpl::vector需要已知类型,我需要将模板模板参数交给它。相反,我使用了支持模板模板参数并隐式派生的自定义类型列表。

struct expensive {};

// derivation list

struct nil {}; // list end
struct Noop {  // do nothing on end of derivation chain
    Noop(expensive& e) {}
};

template<template <typename T> class H, typename L>
struct DL {
    typedef L tail;
    typedef H<typename tail::head> head;
};

template<template <typename T> class H>
struct DL<H, nil> {
    typedef H<Noop> head;
};

// example types

template<class T>
struct A : T {
    A(expensive& e) : T(e) {}
};

template<class T>
struct B : T {
    B(expensive& e) : T(e) {}
};

// derivation chain usage example

typedef DL<A, DL<B, nil> > DerivationChain;

class User : DerivationChain::head
{
public:
    User(expensive& e) : DerivationChain::head(e) {}
};

int main(int argc, char** argv)
{
    expensive e;
    User u(e);
}
于 2009-11-04T03:36:58.117 回答
3

我忍不住想看看如何处理它inherit_linearly。事实证明并没有那么糟糕,恕我直言:

template<class Base, class Self>
struct PolicyWrapper : Base, Self
{
    PolicyWrapper(const ExpensiveType& E)
        : Base(E), Self(E)
    {}
};

struct EmptyWrapper
{
    EmptyWrapper(const ExpensiveType& E)
    {}
};

template <typename PolicyTypeList = boost::mpl::vector<> >
class Host : 
    public inherit_linearly<
       PolicyTypeList, 
       PolicyWrapper<_1, _2>, 
       EmptyWrapper
    >::type
{

typedef typename inherit_linearly<
    PolicyTypeList, 
    PolicyWrapper<_1, _2>, 
    EmptyWrapper
>::type BaseType;

public:
    Host() : BaseType(m_expensiveType)
    {}

private:
    const ExpensiveType m_expensiveType;
};

但是有一个警告:像在 Host ctor 中所做的那样传递对未初始化成员的引用是非常脆弱的。例如,如果有人编写这样的策略:

struct BadPolicy
{
    BadPolicy(const ExpensiveType& E)
    : m_expensiveType(E)
    {}

    ExpensiveType m_expensiveType;
};

坏事会发生,因为 ExpensiveType 的复制 ctor 将被一个未初始化的对象调用。

于 2009-11-04T15:02:32.763 回答
-2

创建一个参数化构造函数并将参数传递给它。通过这种方式,您可以同时实现两件事。1) 构造函数重载 2) 避免调用默认构造函数。

于 2009-11-04T05:38:41.897 回答