Условные псевдонимы шаблонного типа в области шаблонной функции - PullRequest
0 голосов
/ 08 апреля 2019

Скажем, у меня есть N функции, которые имеют одинаковый набор аргументов. Я вызываю фабричный метод с аргументами и некоторым значением, которое позволяет мне выбирать между N функциями.

Неопрятный подход состоит в том, чтобы делать оператор if следующим образом:

if(choice == 1)
    foo_one<T>(args);
else if (choice == 2)
    foo_two<T>(args);
etc

Это нормально, за исключением моего случая, когда количество аргументов велико. Таким образом, заводской метод очень переполнен.

Скажем, у меня есть основная функция, которая вызывает фабричный метод:

int main(int argc, char *argv[])
{
    int choice = 1;
    float arg = 1.5;
    foo_choose(choice, arg);
    return 0;
}

Я имею в виду ветвление аргумента choice и оператора if, устанавливающего псевдоним функции.

Это означает, что мне нужно будет передать свои аргументы только один раз, независимо от того, насколько велик N.

Это может выглядеть примерно так:

template <typename T>
void foo_one(T arg) { std::cout << "foo_one\n"; };

template <typename T>
void foo_two(T arg) { std::cout << "foo_one\n"; };

template <typename T>
void foo_choose(int choice, T arg)
{
    if(choice == 1)
        using foo = foo_one<T>;
    else
        using foo = foo_two<T>;        

    foo(arg);
};

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

Поскольку я мог бы вызывать foo_choose несколько раз с различными значениями для choice, я бы не хотел, чтобы псевдоним foo существовал вне области действия функции.

Этот вопрос предполагает возможно, я не на том пути, но я не уверен.

Ответы [ 3 ]

2 голосов
/ 08 апреля 2019

проблема с

template <typename T>
void foo_choose(int choice, T arg)
{
    if(choice == 1)
        using foo = foo_one<T>;
    else
        using foo = foo_two<T>;        

    foo(arg);
};

Является ли использование операторов утверждениями. Это означает, что они применяются только к области, которая объявлена, поэтому, как только вы доберетесь до foo(arg);, foo выйдет из области видимости, и вы получите ошибку. Вместо того, чтобы получить псевдоним для функции, вы можете получить указатель на нее, а затем вызвать указатель в конце. Это выглядит как

template <typename T>
void foo_choose(int choice, T arg)
{
    using f_ptr = void(*)(T);
    f_ptr foo;
    if(choice == 1)
        foo = foo_one<T>;
    else
        foo = foo_two<T>;        

    foo(arg);
};

Это сопровождается предупреждением о том, что вы должны указать все параметры шаблона функции.

1 голос
/ 08 апреля 2019

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

template <typename T>
void foo_choose(int choice, T arg)
{
    constexpr void (*funcs[])(T) = { foo_one<T>, foo_two<T> };

    assert(choice > 0 && choice < std::extent_v<decltype(funcs)>);

    return funcs[choice](arg);
};

живой пример здесь

Объяснение, почему ваш оригинальный подход не сработал, уже было дано другими ответами здесь Я не думаю, что мог бы добавить к этому & hellip;

0 голосов
/ 08 апреля 2019

Малая точка

Нет необходимости использовать

using foo = foo_one<T>;

с последующим

foo(arg);

Вы можете также использовать

foo_one(arg);

Более важно

Как вы уже заметили, использование каскадных операторов if / else является проблемой обслуживания и легко приводит к ошибкам.

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

Давайте дома, у кого-то еще есть правильная идея показать, что это можно сделать.

...