Создание шаблона с изменяемым типом - PullRequest
0 голосов
/ 04 сентября 2010

Один из методов члена моего класса принимает в качестве аргумента тип перечисления: он вызывает разные побочные эффекты для разных перечислений. Мне было интересно, возможно ли использовать шаблон в качестве справочной таблицы, мне пришло в голову два возможных решения, но ни одно из них не работает:

//// 1 ////

class A {

    public:

    enum AEnum : uint8_t { first, second, ... };

    private:

    template<AEnum N, typename T>
    struct impl {
        static void do_sth(T t) { ... };
    };

    template<typename T>
    struct impl<first, T> {
        static void do_sth(T t) { ... };
    };

    public:


    template<typename T>
    void do_sth(AEnum e, T t) {
        impl<e, T>::do_sth(t);
    }

}

//// 2 ////

class A {

    public:

    enum AEnum : uint8_t { first, second, ... };

    private:

    template<typename T_enum, typename T>
    struct impl {
        static void do_sth(T t) { ... };
    };

    template<typename T>
    struct impl<uint8_t[2], T> { // A::first
        static void do_sth(T t) { ... };
    };

    public:


    template<typename T>
    void do_sth(AEnum e, T t) {
        impl<uint8_t[static_cast<uint8_t>(e) + 1u], T>::do_sth(t);
    }

}

Это действительно плохая идея, чтобы кодировать это таким образом?

@ Оли Чарльзуорт

Что не так с оператором switch?

Поддерживаемые типы второго аргумента do_sth (T) зависят от значения e, например A :: first поддерживает интегралы и контейнеры A :: second STL, например:

    template<typename T>
    void do_sth(AEnum e, T t) {
        switch(e) {
            case first:
                std::cout << &t << std::endl;
                break;
            case second:
                std::cout << t.data() << std::endl;
                break;
            default:
                break;
    }

A a;
a.do_sth(A::first, 0);

Ответы [ 2 ]

2 голосов
/ 04 сентября 2010

Вы должны сделать аргумент AEnum аргументом шаблона для do_sth:


 template<AEnum e, typename T>
    void do_sth(T t) { ... }

... и назвать его как a.do_sth<A::first>(0).

В качестве альтернативы, вы можете написать отдельные функции (do_sth_integral, do_sth_container, ...) или, если существует только один правильный курс действий для конкретного T, выведите «правильное» значение перечисления для данного T, используя приемы метапрограммирования / перегрузки.

Например, вот способ написания двух функций, которые, например, обнаруживают числовые типы и типы контейнеров:


//The extra dummy argument is invalid for types without a nested 
//"iterator" typedef
template<typename T>
void do_sth(T t, typename T::iterator * = 0)
{
    //container type code
}

//The dummy arg is invalid for types without a 
//std::numeric_limits specialization
template<typename T>
void do_sth(T t, 
typename boost::enable_if_c<std::numeric_limits<T>::is_specialized>::type * = 0) 
{
    //numeric type code
}

Конечно, это не удастся, если вы передадите T с итератором typedef и специализацией numeric_limits, или не имеет ни того, ни другого.

Если для определенного T существует только одно разумное действие, и трудно правильно угадать, какую перегрузку следует использовать для неизвестного T, тогда вы можете использовать класс признаков, который должен выполнять пользователь.явно специализировать или просто требовать, чтобы пользователи специализировали "impl" или диспетчерский класс.

Вы не можете написать функцию, которая делает что-то вроде 3.data(), даже если этот кодПуть никогда не вызывается при запуске программы.Компилятор не знает, что он никогда не будет вызван, и в любом случае он нарушает систему типов языка так, чтобы вызвать диагностируемую ошибку.

1 голос
/ 04 сентября 2010

Да, то, что вы написали, не имеет никакого смысла.Создание экземпляров шаблона разрешается во время компиляции, тогда как очевидно, что значение e известно только во время выполнения.

Что не так с оператором switch?

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...