Как вызвать функцию шаблона для каждого типа в списке типов с аргументами (например, другой кортеж)?
Дан список типов std::tuple<T1, T2, T3, ...> и std::tuple, содержащий данные.
template <typename T>
void doSomething (const auto& arg) {
std::cout << __PRETTY_FUNCTION__ << '\n';
}
template <typename T> struct w {T v; w(T _v) : v{_v} {}};
int main () {
using types = std::tuple<int, char, float, double, w<int>, w<float>>; // used as type list
constexpr auto data = std::make_tuple(1, 2, 3.0, 4.0f, w(5.0));
// call doSomething<T>(data) for each type in types
// like
// someFunctor<types>(doSomething, data);
}
Моя текущая идея - это функтор, подобный appreach, который получает список типов для извлечения следующих типов и std::tuple<Ts> имеет operator () для вызова doSomething<T>(args) для каждого из Ts.
template<template<typename...> typename TL, typename... Ts>
struct someFunctor {
template<typename... Args>
static constexpr void operator() (Args&&... args) {
(doSomething<Ts>(std::forward<Args>(args)...), ...);
}
};
Не уверен, что это самый разумный подход. Мозговой туман заблокировал меня до сих пор, чтобы заставить его работать.
const auto & в doSomething предназначено или должно было быть const T &?
@ user17732522 типы и данные не зависят друг от друга. Вызываемая функция будет делать разные вещи с данными, в зависимости от каждого типа типов.
@КрисГ. Я все еще не уверен, что должно произойти. Вы хотите, чтобы doSomething вызывалась для каждого типа в types один раз, а arg каждый раз была ссылкой на data?
@HolyBlackCat это предназначено. Ему нужен каждый T + аргумент(ы), например doSomething<int>(data); doSomething<char>(data); doSomething<w<int>>(data); ...
@user17732522 user17732522 да, действительно! Вместо этого может помочь передача функтора/лямбды.
@КрисГ. Неважно, по какой-то причине я забыл, что вы можете вызывать operator() явно с аргументами шаблона.





Используйте частичную специализацию шаблона, чтобы извлечь тип списка типов, затем используйте выражение fold для вызова doSomething с другими параметрами шаблона.
template<typename Tuple>
struct someFunctor;
template<typename... Args>
struct someFunctor<std::tuple<Args...>> {
template<class T>
constexpr void operator()(T&& x) {
(doSomething<Args>(std::forward<T>(x)), ...);
}
};
using types = std::tuple<int, char, float, double, w<int>, w<float>>;
constexpr auto data = std::make_tuple(1, 2, 3.0, 4.0f, w(5.0f));
someFunctor<types>()(data);
Выглядит довольно мило 康桓瑋! Есть ли способ использовать аргумент шаблона шаблона, чтобы избежать специализаций для контейнеров другого типа, чем std::tuple? Действительно, сравнение типов чрезмерно, как упоминал @user17732522, но теперь, когда я это вижу, это удобно.
@КрисГ. template<template<typename...> class TL, typename... Args> struct someFunctor<TL<Args...>> {
Вот небольшой фрагмент кода, чтобы сделать это:
using types = std::tuple<int, char, float, double, w<int>, w<float>>;
constexpr auto data = std::make_tuple(1, 2, 3.0, 4.0f, w(5.0));
[&]<template<typename...> class TL, typename... Ts>(TL<Ts...>*) {
// Now have a parameter pack of `Ts`
(doSomething<Ts>(data), ...);
}((types*) nullptr);
И превратилась в повторно используемую функцию, вы можете сделать что-то вроде:
template<typename types, typename Functor, typename... Args>
constexpr void mapTypeList(Functor&& f, Args&&... args) {
[&]<template<typename...> class TL, typename... Ts>(TL<Ts...>*) {
((void) f.template operator()<Ts>(std::forward<Args>(args)...), ...);
}(static_cast<types*>(nullptr));
}
// You need to wrap `doSomething` in a lambda
mapTypeList<types>([]<typename T>(const auto& data) { doSomething<T>(data); }, data);
// or maybe capturing data:
mapTypeList<types>([&]<typename T>() { doSomething<T>(data); });
Пример использования: https://godbolt.org/z/53x44bGvK
Почему кортеж типов отличается от типа кортежа значений? В вашем примере он даже имеет разную длину. Что должно произойти, если типы элемента не совпадают? Другими словами, почему вы хотите
someFunctor<types>(doSomething, data);вместоsomeFunctor(doSomething, data);?