¿Es posible crear un constructor compuesto sin tuplas?

Sé que puedo expandir un paquete de parámetros de tuplas en una plantilla variada de clases base como esta:

#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()
    };
}

Sin embargo, encuentro esa sintaxis engorrosa. ¿Hay alguna forma de evitar las tuplas por completo?

Edit:

Algo más parecido a esto:

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

Ambos parámetros se pasan a ComponentA, ninguno a ComponentB.

Editar 2

Entonces tengo algo como esto:

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!")
    };

Sin embargo, no he podido averiguar cómo eliminar las dos tuplas de la CompositeClass constructor. ¿Cómo se puede hacer esto?

preguntado el 01 de febrero de 12 a las 22:02

1 Respuestas

Al parecer, si reduce un poco los períodos de rociado en todo el lugar, ¡debería estar bien! Los paquetes de parámetros no son nada especial: simplemente se expanden en una lista separada por comas. Es decir, si cambia su código para convertirlo en el siguiente, debería estar bien:

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

Excepto por agregar dos puntos delante de la lista de inicializadores, solo eliminé algunos "..." en exceso. Esto funciona bien siempre que todos los argumentos de su plantilla sean en realidad tipos de clase y siempre que sean diferentes. Obviamente, los tipos que no son clases no se pueden usar como bases. Si necesita el mismo tipo varias veces como base, debe heredarlos indirectamente por qué un tipo auxiliar que les da un número. Generar los números es un poco complicado las primeras veces que lo haces, pero tampoco nada realmente mágico.

Ampliando la pregunta editada: ¿Quiere decir que desea pasar listas de argumentos a cada constructor de clase base individual? Si está bien pasar std:tuple<...> como argumentos a tu CompositeClass esto también es factible. Esencialmente, necesitas transformar cada std::tuple<...> en una lista de argumentos. Esto también requiere la generación de dichos índices. Al menos empezaría con una base auxiliar que necesita un std::tuple<...> como argumento y reenvía los miembros como argumentos a la base. Esto usaría algo similar al código anterior para el CompositeClass y el truco principal estaría en una clase auxiliar:

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>())...
     {}
};

Implementando el constructor de AuxiliaryBase esencialmente requiere que haya una facilidad para crear una secuencia de enteros a partir de 0 a std::tuple_size<Tuple>::value. Esto requiere algunos ajustes, pero definitivamente es factible. Para que estén disponibles, se pasa un parámetro auxiliar (no estoy seguro de si esto se puede evitar; bueno, probablemente se pueda empaquetar como un tipo con el std::tuple<...> si esto debe evitarse). Con esto, el constructor de la clase base es bastante sencillo:

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

No lo he probado, pero algo así debería funcionar. Lo que esta versión no hace es el reenvío perfecto de los miembros en el std::tuple<...>: para esto necesitaría una variación de std::forward<>() que toma tres argumentos que utilizan las calificaciones de referencia del tipo externo para determinar qué tipo de referencia debe enviarse para el miembro. No lo he probado, pero probablemente también sea un ejercicio interesante.

No he intentado compilar este ejemplo en particular, pero definitivamente he hecho algo como esto en el pasado: si miras el diapositivas por la presentación que di en el ACCU conferencia en 2010 encontrará todos los detalles sobre cómo hacer esto (incluido cómo crear una secuencia de números enteros; muy poco de esto, en realidad).

Respondido 02 Feb 12, 03:02

Al usar esta CompositeClass, debo usar tuplas para pasar múltiples argumentos a una sola clase base. Actualicé mi pregunta, con suerte para mostrar el efecto que estoy tratando de lograr. - Timesquare

¿Hay alguna forma de construir CompositeClass sin usar tuplas y aún así pasar argumentos a los constructores de componentes? - Timesquare

Por supuesto. Sin embargo, necesitará alguna forma de determinar en tiempo de compilación qué argumentos van a qué base. Puede utilizar, por ejemplo, una clase especial que luego puede detectar como separador. O una fuerza de rasgo de tipo en cada base. Para facilitar las cosas, probablemente todavía crearía una tupla auxiliar, pero el usuario no tendría que hacerlo. La pregunta es: ¿cómo quiere saber cuántos argumentos toma cada base? - Dietmar Kühl

Puede ser posible crear un rasgo de tipo que detecte cuántos argumentos consume un constructor de un paquete de parámetros. Todavía no he hecho esto. Una vez que sepa qué parámetros van a dónde puede reenviarlos a las bases. Como mencioné, internamente todavía usaría tuplas para hacerlo más simple, pero también se puede hacer sin ellas. - Dietmar Kühl

No es la respuesta que estás buscando? Examinar otras preguntas etiquetadas or haz tu propia pregunta.