Диспетчеризация на основе типов C++ для встроенных типов

Мне нужна функция диспетчера, как показано ниже

template<typename T>
T dispatcher() {
    // if T is double
    return _func_double();
    // if T is int
    return _func_int();
    // if T is char*
    return _func_char_pointer();
}

и будет использоваться, как показано ниже

// some code that use above dispatcher
template<typename T1, typename T2, typename T3>
void do_multiple_thing(T1 *a1, T2 *a2, T2 *a3) {
    *a1 = dispatcher<T1>();
    *a2 = dispatcher<T2>();
    *a3 = dispatcher<T3>();
}

Не могли бы вы рассказать мне, как этого добиться?

P.S.
- достаточно решения только для встроенных типов. - допустима как предварительная обработка, так и использование шаблона.

Стоит ли изучать PHP в 2023-2024 годах?
Стоит ли изучать PHP в 2023-2024 годах?
Привет всем, сегодня я хочу высказать свои соображения по поводу вопроса, который я уже много раз получал в своем сообществе: "Стоит ли изучать PHP в...
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
В JavaScript одним из самых запутанных понятий является поведение ключевого слова "this" в стрелочной и обычной функциях.
Приемы CSS-макетирования - floats и Flexbox
Приемы CSS-макетирования - floats и Flexbox
Здравствуйте, друзья-студенты! Готовы совершенствовать свои навыки веб-дизайна? Сегодня в нашем путешествии мы рассмотрим приемы CSS-верстки - в...
Тестирование функциональных ngrx-эффектов в Angular 16 с помощью Jest
В системе управления состояниями ngrx, совместимой с Angular 16, появились функциональные эффекты. Это здорово и делает код определенно легче для...
Концепция локализации и ее применение в приложениях React ⚡️
Концепция локализации и ее применение в приложениях React ⚡️
Локализация - это процесс адаптации приложения к различным языкам и культурным требованиям. Это позволяет пользователям получить опыт, соответствующий...
Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
3
0
417
4
Перейти к ответу Данный вопрос помечен как решенный

Ответы 4

Ответ принят как подходящий

Если у вас есть компилятор с поддержкой C++ 17, этот фрагмент кода должен работать:

template<typename T>
T dispatcher() {
    // if T is double
    if constexpr (std::is_same<T, double>::value)
        return _func_double();
    // if T is int
    if constexpr (std::is_same<T, int>::value)
        return _func_int();
    // if T is char*
    if constexpr (std::is_same<T, char*>::value)
        return _func_char_pointer();
}

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

//only needed for static assert
template<typename T>
struct always_false : std::false_type {};


template<typename T>
T dispatcher() 
{ 
//to make sure that on type you didn't overload you will have exception
    throw std::exception("This type was not overloaded")
//static assert that will provide compile time error
    static_assert(always_false<T>::value , "You must specialize dispatcher for your type");
}
//or to get compile time error without static assert 
template<typename T>
T dispatcher() = delete; //the simplest solution

template<>
double dispatcher<double>() 
{
    return _func_double();
}
//... and so on for all other functions
static_assert(false, "!") недействителен и может сработать, даже если не создан. Вы должны использовать что-то вроде static_assert(always_false<T>::value, "!")template <typename> struct allways_false : std::false_type{};), см. что-то-правильный-способ-вызвать-статическое-assertfalse.
Jarod42 10.08.2018 09:00

о, я этого не знал. Так что, вероятно, будет лучше и проще просто использовать = delete

wdudzik 10.08.2018 09:08

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

Jarod42 10.08.2018 09:11

Поэтому я добавил случай со статическим утверждением и struct always_false.

wdudzik 10.08.2018 09:20

В C++ 17 вы можете комбинировать if constexpr и std::is_same:

template<typename T>
T dispatcher() {
    if constexpr (std::is_same<T, double>::value) {
        return _func_double();
    } else if constexpr (std::is_same<T, int>::value) {
        return _func_int();
    } else if constexpr (std::is_same<T, char*>::value) {
        return _func_char_pointer();
    } else {
        return {}; // or static_assert(always_false<T>::value); ?
    }
}

Раньше вы могли использовать специализацию или отправку тегов с перегрузкой:

template<typename T>
T dispatcher() {
    return {}; // or static_assert(always_false<T>::value); ?
}

template<>
double dispatcher() {
    return _func_double();
}

template<>
int dispatcher() {
    return _func_int();
}

template<>
char* dispatcher() {
    return _func_char_pointer();
}

или

template<typename T> struct tag {};


template<typename T>
T dispatcher(tag<T>) = delete; // or { return {}; } ?

double dispatcher(tag<double>) { return _func_double(); }

int dispatcher(tag<int>) { return _func_int(); }

char* dispatcher(tag<char*>) { return _func_char_pointer(); }

// some code that use above dispatcher
template<typename T1, typename T2, typename T3>
void do_multiple_thing(T1 *a1, T2 *a2, T2 *a3) {
    *a1 = dispatcher(tag<T1>());
    *a2 = dispatcher(tag<T2>());
    *a3 = dispatcher(tag<T3>());
}

Вы забыли про auto.

Marek R 10.08.2018 08:31

@MarekR: Где? Тип возврата OP кажется всегда T. но действительно, если тип возврата _func_double() / _func_int() / _func_char_pointer() менее однороден, можно использовать decltype(auto).

Jarod42 10.08.2018 08:40
template <typename T>
T fetch_magic_value();

template <>
int fetch_magic_value<int>() { return 23; }

template <>
char fetch_magic_value<char>() { return ' '; }

template <>
std::string fetch_magic_value<std::string>() { return "tada"; }

template<typename T>
void do_multiple_thing(T *x) {
  *x = fetch_magic_value<T>();
}

template<typename T, typename... Args>
void do_multiple_thing(T *first, Args *...args)
{
    do_multiple_thing(first);
    do_multiple_thing(args...);
}

https://wandbox.org/permlink/v2NMhoy8v3q5VhRf

Версия C++ 17: https://wandbox.org/permlink/0pi08jvYF5vlIpud

Если вам нужно универсальное решение для написания таких диспетчеров, можно использовать что-то вроде этого:

// calls the first function that has return type `T` with no arguments
template <class T, class F, class... Fs>
constexpr T call_by_return_type(F&& f, Fs&&... fs) {
    if constexpr (std::is_same_v<T, std::invoke_result_t<F>>) {
        return f();
    } else if constexpr (sizeof...(fs) > 0) {
        return call_by_return_type<T>(std::forward<Fs>(fs)...);
    } else {
        static_assert(
            sizeof(T) == 0,
            "`T` must match at least one function's return type"
        );
    }
}

А затем вы можете создать диспетчеров как комбинацию функций (может быть любой объект-функция, который вызывается без аргументов):

template <class T>
constexpr T dispatcher() {
    return call_by_return_type<T>(
        _func_double,
        _func_int,
        _func_char_pointer
    );
}

Пример на godbolt.org


Примечание: Я предполагал, что у вас уже есть функции _func_<return-type>, которые необходимо сгруппировать для формирования диспетчера, иначе я мог бы подумать о более элегантных интерфейсах.

Другие вопросы по теме