Как мне вызвать конструктор не по умолчанию для каждого унаследованного типа из списка типов?

StackOverflow https://stackoverflow.com/questions/1671297

Вопрос

Я использую список типов 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;
};

Можно ли определить конструктор 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;
};

Тот самый повышение::mpl:: для каждого алгоритм выглядит многообещающим, но я не могу взять в толк, как его использовать для решения этой проблемы.

Это было полезно?

Решение

Я не мог устоять перед искушением посмотреть, как это можно сделать с помощью 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;
};

произойдут плохие вещи, так как ctor копирования ExpensiveType будет вызван с неинициализированным объектом.

Другие советы

Если вам нужно такое поколение, я могу только порекомендовать почитать книгу Александреску. Современный дизайн на C ++.Существует целая глава, посвященная созданию иерархии из списка типов.Вы также можете найти его на веб-сайте Локи: Генераторы иерархии;хотя вы будете скучать по диаграммам и пояснениям, а также по самому процессу.

Для вашей конкретной проблемы это кажется довольно простым.

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

Конечно, это всего лишь набросок.Основная проблема здесь - это расширяемость.Если вы прочтете книгу Александреску, вы узнаете гораздо больше, а если у вас нет времени, ознакомьтесь с исходным кодом, возможно, это именно то, что вам нужно.

Есть способы сделать это непосредственно из mpl::vector, единственное, что нужно понимать, это то, что вы не можете сделать это с помощью big MI single layer, но вы можете добавить много слоев.

Здесь я решил не добавлять сложности на уровне Политик (они не шаблонизированы) и вместо этого полагаться на MI (dual) на каждом уровне.Вы могли бы сделать это чисто линейным, но шаблонизация ваших политик означает, что вы не можете определить их в исходном файле.

Также обратите внимание, что этот подход может быть адаптирован для принятия mpl::vector напрямую, но это потребовало бы использования операций программирования на основе меташаблонов: back, pop_back и empty по крайней мере, это может больше запутать код, чем помочь на самом деле.

Как упоминалось в комментарии, вам нужно связать вызовы конструктора по цепочке.Для этого каждый тип в цепочке вывода должен знать, от какого типа он получен - чтобы обеспечить возможность произвольных последовательностей вывода, нам нужно создать шаблоны этих типов, чтобы их базой мог быть любой тип.
Это позволяет нам ссылаться на базу и явно вызывать ее конструкторы.

Я набросал базовый пример, но в итоге не использовал 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);
}

Создайте параметризованный конструктор и передайте ему параметры.Таким образом, вы могли бы достичь двух целей одновременно.1) Перегрузка конструктора 2) Избегайте вызова конструктора по умолчанию.

Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top