Возможно ли создать составной конструктор без кортежей? - PullRequest
1 голос
/ 02 февраля 2012

Я знаю, что могу расширить пакет параметров кортежей на вариационный шаблон базовых классов как это:

#include <tuple>
struct ComponentA {
    int foo;
    int bar;
    ComponentA(std::tuple<int, int> && pack):
        foo(std::get<0>(pack)),
        bar(std::get<1>(pack))
    {}
};

struct ComponentB {
    ComponentB(std::tuple<> && pack)
    {}
};

template<typename ... Bases>
struct CompositeClass: public Bases... {
    template<typename ... Arguments>
    CompositeClass(Arguments &&... arguments):
        Bases(std::forward<Arguments>(arguments))...

    {}
};

int main() {
    CompositeClass<ComponentA, ComponentB> composite{
        std::make_tuple(100, 100),
        std::make_tuple()
    };
}

Однако я считаю этот синтаксис громоздким. Есть ли способ вообще избежать кортежей?

Edit:

Нечто подобное:

struct ComponentA {
    int foo;
    int bar;
    ComponentA(int a, int b):
        foo(a),
        bar(b)
    {}
};

struct ComponentB {
    ComponentB()
    {}
};

template<typename ... Bases>
struct CompositeClass: public Bases... {
    template<typename ... ... Arguments>
    CompositeClass(Arguments &&... ... arguments):
        Bases(std::forward<Arguments>(arguments)...)...
    {}
};

int main() {
    CompositeClass<ComponentA, ComponentB> composite{100, 100};
}

Оба параметра передаются в ComponentA, но не в ComponentB.

Редактировать 2

Итак, у меня есть что-то вроде этого:

template <int ...>
struct SequenceContainer {};

template <int, typename>
struct AppendIntToSequence;

template <int Value, int ... Sequence>
struct AppendIntToSequence<Value, SequenceContainer<Sequence...>> {
    typedef SequenceContainer<Sequence..., Value> type;
};

template<int End>
struct MakeSequence:
    AppendIntToSequence<End - 1, typename MakeSequence<End - 1>::type> {};

template<>
struct MakeSequence<0> {
    typedef SequenceContainer<> type;
};

struct ComponentA {
    static const int parameters = 2;
    ComponentA(int && a, int && b) {
        std::cout << a << b << std::endl;
    }
};

struct ComponentB {
    static const int parameters = 1;
    ComponentB(const char * string) {
        std::cout << string << std::endl;
    }
};

template <typename Base>
struct TupleConstructor: Base {
    template <typename ... Arguments, int ... Sequence>
    TupleConstructor(std::tuple<Arguments...> &&, SequenceContainer<Sequence...> const &);
};

template <typename Base>
template <typename ... Arguments, int... Sequence>
TupleConstructor<Base>::TupleConstructor(std::tuple<Arguments...> && arguments, SequenceContainer<Sequence...> const &):
    Base(std::forward<Arguments>(std::get<Sequence>(arguments))...)
{}


template <typename ... Components>
struct CompositeClass: public TupleConstructor<Components>... {
    template <typename ... Arguments>
    CompositeClass(Arguments &&... arguments):
        TupleConstructor<Components>(
            std::forward<Arguments>(arguments),
            typename MakeSequence<std::tuple_size<Arguments>::value>::type{}
        )...
    {}
};

int main()
{
    CompositeClass<ComponentA, ComponentB> composite{
        std::tuple<int &&, int &&>(100,100),
        std::tuple<const char *>("Hello World!")
    };

Однако мне не удалось выяснить, как удалить два кортежа из конструктора CompositeClass. Как это можно сделать?

1 Ответ

2 голосов
/ 02 февраля 2012

Кажется, если вы немного сократите периоды разбрызгивания повсюду, у вас должно быть все в порядке!Пакеты параметров не являются чем-то особенным: они просто развернуты в отдельный список запятых.То есть, если вы измените свой код, чтобы он стал таким, как показано ниже, у вас должно быть все в порядке:

template <typename... Bases>
struct CompositeClass: public Bases... {
    template <typename... Arguments>
    CompositeClass(Arguments&&... arguments):
        Bases(std::forward<Arguments>(arguments))...
    {}
};

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

Расширение отредактированного вопроса: Вы хотите передать списки аргументов каждому отдельному конструктору базового класса?Если можно передать std:tuple<...> в качестве аргументов вашему CompositeClass, это тоже выполнимо.По сути, вам нужно преобразовать каждый std::tuple<...> в список аргументов.Это также требует генерации указанных индексов.Я бы, по крайней мере, начал со вспомогательной базы, которая принимает std::tuple<...> в качестве аргумента и передает членов в качестве аргументов в базу.Это будет использовать что-то похожее на код выше для CompositeClass, а основной трюк будет во вспомогательном классе:

template <int... Numbers> struct Sequence {};
template <int N> struct MakeSequence;

template <typename Base>
struct AuxiliaryBase: Base {
    template <typename Tuple, int... Numbers>
    AuxiliaryBase(Tuple&&, Sequence<Numbers...> const&);
};

template <typename... Bases>
struct CompositeClass: public AuxiliaryBase<Bases>... {
     template <typename... Args>
     CompositeClass(Args&&... arguments):
         AuxiliaryBase<Bases>(std::forward<Args>(arguments),
                              typename MakeSequence<std::tuple_size<Args>::size>())...
     {}
};

Реализация конструктора AuxiliaryBase по существу требует наличия средства для созданияпоследовательность целых чисел от 0 до std::tuple_size<Tuple>::value.Это займет немного настройки, но, безусловно, выполнимо.Чтобы сделать их доступными, передается вспомогательный параметр (я не уверен, что этого можно избежать; ну, возможно, его можно упаковать как тип с std::tuple<...>, если этого следует избегать).При этом конструктор базового класса довольно прост:

    template <typename Base>
    template <typename Tuple, int... Numbers>
    AuxiliaryBase<Base>::AuxiliaryBase(Tuple&& tuple, Sequence<Numbers...> const&):
        Base(std::get<Numbers>(tuple)...)
    {}

Я не проверял его, но что-то в этом роде должно действительно работать.Чего эта версия не делает, так это идеальной пересылки членов в std::tuple<...>: для этого вам понадобится вариант std::forward<>(), который принимает три аргумента, которые используют ссылочные квалификации внешнего типа, чтобы определить, какой тип ссылочных потребностейбыть вперед для члена.Я не пробовал это, но это, вероятно, тоже интересное упражнение.

Я не пытался скомпилировать этот конкретный пример, но я определенно делал что-то подобное в прошлом: если вы посмотрите слайды для презентации, которую я дал на конференции ACCU в 2010 , вы найдете все подробности о том, как это сделать (включая создание последовательности целых чисел;очень мало этого, на самом деле).

...