Мне нужна функция диспетчера, как показано ниже
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.
- достаточно решения только для встроенных типов.
- допустима как предварительная обработка, так и использование шаблона.
Если у вас есть компилятор с поддержкой 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
о, я этого не знал. Так что, вероятно, будет лучше и проще просто использовать = delete
Я думаю, что по крайней мере у некоторых компиляторов были проблемы со специализацией удаленных функций.
Поэтому я добавил случай со статическим утверждением и struct always_false.
В 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
.
@MarekR: Где? Тип возврата OP кажется всегда T
. но действительно, если тип возврата _func_double()
/ _func_int()
/ _func_char_pointer()
менее однороден, можно использовать decltype(auto)
.
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
);
}
Примечание: Я предполагал, что у вас уже есть функции _func_<return-type>
, которые необходимо сгруппировать для формирования диспетчера, иначе я мог бы подумать о более элегантных интерфейсах.
static_assert(false, "!")
недействителен и может сработать, даже если не создан. Вы должны использовать что-то вродеstatic_assert(always_false<T>::value, "!")
(сtemplate <typename> struct allways_false : std::false_type{};
), см. что-то-правильный-способ-вызвать-статическое-assertfalse.