Шаблоны с переменным числом аргументов пакет один класс использует


Имея следующий пример:

/* Signal Container */
template <typename Ret> class Signal;

template <typename Ret, typename... Args>
class Signal< Ret (Args...) >
{
    /* Following Implementation... */
};

/* Emitter Type */
template < template <typename Ret, typename... Args> Signal< Ret (Args...) > ... Sig>
class Emitter
{
    // using Signals = std::tuple<Sig...>;

    /* Following Implementation... */
};

/* Signals */
using Signal_A = Signal<void()>;
using Signal_B = Signal<void(int)>;
using Signal_C = Signal<void(int, float)>;

/* Desired Usage */
class MyType : public Emitter<Signal_A, Signal_B, Signal_C>
{

};

Я хотел бы иметь возможность наследовать от типа Emitter, который принимает 1 (0?) или несколько параметров шаблона типа Signal (и только Signal). Signal является шаблонным типом, и его определение отличается от каждого типа, передаваемого в пакет аргументов Emitter.

Я попробовал текущий подход на MinGW, но я получаю эти сообщения об ошибках, и я немного потерялся:

/* Line: template < template <typename Ret, typename... Args> Signal< Ret (Args...) > ... Sig> */
main.cpp|15|error: expected 'class' before 'Signal'|


/* Line: template < template <typename Ret, typename... Args> Signal< Ret (Args...) > ... Sig> */
main.cpp|15|error: expected '>' before '<' token|


/* Line: class MyType : public Emitter<Signal_A, Signal_B, Signal_C> */
main.cpp|29|error: wrong number of template arguments (3, should be 1)|


/* Linne: class Emitter */
main.cpp|16|error: provided for 'template<template<class Ret, class ... Args> class Signal> class Emitter'|

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

доступные компиляторы: MinGW GCC 4.9.2 (также 5.1.0)

3   2   2015-07-05 04:44:19

3 ответа:

Вы не можете сделать то, что вы хотите в C++11 или C++14 простым способом. Понятия могут дать нам что-то, но пока ваши аргументы шаблона должны быть либо типом, либо шаблоном класса, либо значением. В вашем случае вам нужен пакет сигналов, который может быть указан только как:

template <typename... Sigs>
class Emitter;

Внутри класса вы можете затем использовать static_assert, чтобы проверить, что все они Signals:

static_assert(all_true<is_signal<Sigs>::value...>::value, "Emitter must use only Signals");

Вам придется написать признак типа для is_signal и предоставить метафункцию для all_true. Один из примеров последнего можно найти здесь

Вам не нужно сопоставлять все внутренние типы Signal в шаблоне класса Emitter. Вы просто должны заявить, что это шаблон, принимающий параметры. После этого вам нужно будет повторить до минимального числа Signal<T>, которое вы можете разрешить в своем приложении. Предположим, что минимум равен единице, вот решение.

/* Signal Container */
template <typename Ret>
class Signal;

template <typename Ret, typename... Args>
class Signal<Ret(Args...)> {};

/* Emitter Type */
template <typename... Args>
class Emitter;

template <typename T>
class Emitter<Signal<T> > {
    // definition of class with single signal
};

template <typename T, typename... Args>
class Emitter<Signal<T>, Args...> {
    // definition of class with MORE THAN one signal
};

/* Signals */
using Signal_A = Signal<void()>;
using Signal_B = Signal<void(int)>;
using Signal_C = Signal<void(int, float)>;

/* Desired Usage */
class MyType : public Emitter<Signal_A, Signal_B, Signal_C> {};

int main() {
    MyType x;
}

Я публикую это как ответ, потому что он действительно соответствует требованиям в вопросе. Даже несмотря на то, что я отметил правильный ответ. Я думаю, что уместно опубликовать мой окончательный подход.

#include <iostream>

/* Signal Container */
template <typename Ret> class Signal;

template <typename Ret, typename... Args>
class Signal< Ret (Args...) >
{
    /* Following Implementation... */
};

namespace {
    /* Signal Type Traits */
    template < typename T >
    struct IsSignal { static constexpr bool Value = false; };

    template < typename T >
    struct IsSignal< Signal< T > > { static constexpr bool Value = true; };

    /* Signal Validation */
    template < bool V, typename... Args >
    struct AreSignals
    {
        static constexpr bool Value = V;
    };

    template < bool V, typename T, typename... Args >
    struct AreSignals< V, T, Args... >
    {
        static constexpr bool Value = AreSignals< V && IsSignal< T >::Value, Args... >::Value;
    };

}

/* Emitter Type */
template < typename... Args >
class Emitter
{
    // Block unsupported signals
    static_assert( AreSignals<true, Args...>::Value, "Unsupported signal type" );

    using Signals = std::tuple<Args...>;

    /* Following Implementation... */
};

using Signal_A = Signal<void()>;
using Signal_B = Signal<void(int)>;
using Signal_C = Signal<void(int, float)>;

class MyType : public Emitter<Signal_A, Signal_B, Signal_C>
{

};

int main(int argc, char **argv)
{
    std::cout << AreSignals<true, Signal_A, Signal_B, Signal_C>::Value << "\n"; // 1 (true)

    std::cout << AreSignals<true, Signal_A, int, Signal_B, Signal_C>::Value << "\n"; // 0 (false)

    return EXIT_SUCCESS;
}