代码之家  ›  专栏  ›  技术社区  ›  Steve Guidi

如何从类型列表中为每个继承的类型调用非默认构造函数?

  •  4
  • Steve Guidi  · 技术社区  · 16 年前

    我使用boost类型列表以以下方式实现策略模式。

    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;
    };
    

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

    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 回复  |  直到 16 年前
        1
  •  4
  •   Matthieu M.    16 年前

    如果你想要这样的一代,我只能推荐你读一读亚历山德雷斯库的书 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(dual)。您可以将其完全线性化,但模板化策略意味着您无法在源文件中定义它们。

    向量 back , pop_back empty 至少,这可能会使代码变得更加混乱,而不是它们实际的帮助。

        2
  •  3
  •   Georg Fritzsche    16 年前

    如注释中所述,您需要链接构造函数调用。为此,派生链中的每个类型都必须知道其派生自什么类型-为了允许任意派生序列,我们需要使该类型成为模板,以便它们的基可以是任何类型。

    我列举了一个基本示例,但最终没有使用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);
    }
    
        3
  •  3
  •   Éric Malenfant    16 年前

    我忍不住想看看如何用它来做 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;
    };
    

    不过有一个警告:将引用传递给未初始化的成员(如在主机中所做的操作)是非常脆弱的。 例如,如果编写的策略如下所示:

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

    不好的事情会发生,因为ExpensiveType的copy-ctor将被一个未初始化的对象调用。

        4
  •  -2
  •   Sachin    16 年前

    创建一个参数化构造函数并将参数传递给它。这样你可以同时完成两件事。 2) 避免调用默认构造函数。