Как я могу обработать вариативную шаблонную функцию в C++ для обработки нескольких вхождений одного и того же типа в пакете параметров шаблона, а затем передать эти аргументы в другую вариативную функцию?
У меня есть следующая функция шаблона с переменным числом аргументов, которая принимает несколько аргументов и печатает их:
#include <iostream>
template<typename... Args>
void run(Args... var)
{
(std::cout << ... << var) << std::endl;
}
Я хочу создать еще одну функцию шаблона с переменным числом аргументов, ProcessArgs, которая:
Например, если я вызываю процессArgs<int, double, int, int, double>(), я хочу, чтобы он:
Вот желаемый результат:
int main()
{
processArgs<int, double, int, int, double>();
// Should result in calling: run(42, 3.14, 43, 44, 4.14), i don't care what the numbers are, it's just random
return 0;
}
Как я могу реализовать эту функциюprocessArgs для достижения описанного поведения?
@NathanOliver Различные типы начинаются с разных значений, все случайны
@Kitiara На самом деле это может быть не так уж и плохо реализовать. Посмотрим, что получится. Возможно, через некоторое время мне будет с чего начать. Есть ли диапазон случайных значений, которые вам нужны для каждого типа? Я предполагаю, что не до максимума, учитывая, что вы хотите каждый раз увеличивать его на единицу.
@NathanOliver Да, макса нет.
Создайте одну новую структуру с вашими аргументами в качестве членов и используйте ее в качестве аргумента шаблона.
Ваша главная цель — передать аргумент нужным вам способом, а не создавать случайные значения, верно? Судя по вашей текущей формулировке, вам на самом деле нужны были не «случайные» значения, а значения, которые были установлены пользователем, и ваша главная задача заключалась в том, чтобы найти способ передавать и обрабатывать аргументы определенным образом, как вы сказали: «Я не мне все равно, какие цифры, "
@Алан Да, это правильно. Значения были просто примером, возможно, вопрос был не таким информативным. Предоставленные решения действительно являются решениями для примера в вопросе, однако они не решают мою главную проблему.
@Алан, я изменил описание проблемы и пример результата. Теперь это намного информативнее.
Новый ответ на основе обновления вопроса:
По сути, у вас будет та же форма шаблона для processArgs
, что и для run
, вам просто нужно вызвать run с пакетом аргументов. Это выглядит как
template<typename... Args>
void processArgs(Args... args)
{
run(args...);
}
Если вы хотите идеально пересылать все, что, похоже, не так, поскольку у вас строковые литералы распадаются на указатели, тогда вам нужно будет
template<typename... Args>
void processArgs(Args&&... args)
{
run(std::forward<Args>(args)...);
}
Старый ответ:
Вот один из способов получить что-то вроде того, что вы хотите.
Сначала мы начнем со вспомогательных функций
template<std::integral T>
T random_start()
{
std::random_device rd;
std::mt19937 gen(rd());
std::uniform_int_distribution<T> dis(1,1000); // use whatever range you want
return dis(gen);
}
template<std::floating_point T>
T random_start()
{
std::random_device rd;
std::mt19937 gen(rd());
std::uniform_real_distribution<T> dis(1.0, 2.0);
return dis(gen);
}
Получается случайное целое число или число с плавающей запятой. Затем мы можем использовать это в шаблоне функции с состоянием в форме
template<typename T>
T& get_val()
{
static T val = random_start<T>();
return val;
}
Эта функция создает static T
для каждого T
, находящегося в Args
из processArgs
. Кстати, давайте представим processArgs
:
template<typename... Args>
void processArgs()
{
std::tuple<Args...> params{(get_val<Args>()++)...};
std::apply([](auto... args) { run(args...); }, params);
}
Может показаться странным, что я ввел кортеж, а затем должен использовать apply
со вспомогательной лямбдой для вызова run
, но «простое» решение
template<typename... Args>
void processArgs()
{
run(get_val<Args>()++...);
}
имеет недостаток в том, что порядок вычисления аргументов функции не указан и обычно выполняется слева направо в Windows и справа налево в *Nix. Это означает, что в некоторых системах значения будут «уменьшаться» в порядке печати при вызове функции. Вы можете увидеть это на этом живом примере.
Итак, сложив все это вместе, мы получим:
template<typename... Args>
void run(Args... var)
{
((std::cout << var << std::endl), ...);
}
template<std::integral T>
T random_start()
{
std::random_device rd;
std::mt19937 gen(rd());
std::uniform_int_distribution<T> dis(1,1000); // use whatever range you want
return dis(gen);
}
template<std::floating_point T>
T random_start()
{
std::random_device rd;
std::mt19937 gen(rd());
std::uniform_real_distribution<T> dis(1.0, 2.0);
return dis(gen);
}
template<typename T>
T& get_val()
{
static T val = random_start<T>();
return val;
}
template<typename... Args>
void processArgs()
{
//run(get_val<Args>()++...);
std::tuple<Args...> params{(get_val<Args>()++)...};
std::apply([](auto... args) { run(args...); }, params);
}
int main()
{
processArgs<int, double, int, int, double>();
}
и в этом живом примере он дает результат
506
1.29876
507
508
2.29876
Спасибо за решение моего предыдущего вопроса. Я понял, что моя главная цель не была четко сформулирована. Вместо создания случайных значений моя цель — передать конкретные значения в функцию шаблона с вариационными значениями, но это пошло не так. Например, я хочу вызватьprocessArgs("text", 4.6, 'c', 4, "more text", 7.8, 'd') и передать эти точные значения функции запуска. Я обновил свой вопрос, чтобы более четко отразить это намерение.
@Kitiara Я добавил обновление, которое должно делать то, что вы просили в своем обновлении.
Да, это работает для случая (std::cout << ... << var) << std::endl;
, но это не работает, если мы держим std::function векторов, где каждая функция требует разных параметров.
@Kitiara Можете ли вы привести пример?
@Kitiara Обратите внимание, что вам не следует редактировать свой вопрос так, чтобы это меняло всю суть вопроса, если уже опубликовано 2 ответа. Не стесняйтесь задавать новый отдельный вопрос для вашего вопроса с другим намерением. Как правило, если новый дополнительный вопрос существенно отличается от исходного вопроса, следует задать новый вопрос, чтобы уже опубликованные ответы не были признаны недействительными.
Вы можете использовать шаблон класса и член данных static
для достижения желаемого:
#include <iostream>
#include <iomanip>
//wrapper
template<typename T>
struct Wrapper
{
static T value;
Wrapper()
{
value++;
}
};
template<typename T> T Wrapper<T>::value{};
//specialization for random values for your types. Use random values here.
template<> int Wrapper<int>::value = 42;
template<> double Wrapper<double>::value = 3.14;
template<typename... Args>
void run(Args... var)
{
((std::cout << var << ' '), ...) <<std::endl;
}
template<typename... T> void processArgs()
{
run(std::forward<T>((Wrapper<T>().value)-1)...);
}
int main()
{
processArgs<int, double, int, int, double>();
return 0;
}
Я думаю, вам нужно специализировать весь класс, а не только элемент данных. Я получаю сообщение об ошибке дублирующейся инициализации.
@NathanOliver Это работает в рабочей демо. Я посмотрю, что говорит стандарт, и, возможно, задам новый вопрос.
Ага, здесь нет: coliru.stacked-crooked.com/a/4d9ec7c5ccd25754. ленивый
@NathanOliver Я создам новый вопрос. Тем временем я отредактирую этот ответ, чтобы специализировать весь класс.
Похоже, что GCC и MSVC ошибаются, clang компилируется.
@NathanOliver Ошибка возникает только тогда, когда мы используем inline
со статическим элементом данных. Если мы используем старый способ определения вне класса, тогда gcc и msvc также начнут принимать программу. Демо
@user12002570 user12002570 Спасибо за решение моего предыдущего вопроса. Я понял, что моя главная цель не была четко сформулирована. Вместо создания случайных значений моя цель — передать конкретные значения в функцию шаблона с вариационными значениями, но это пошло не так. Например, я хочу вызватьprocessArgs("text", 4.6, 'c', 4, "more text", 7.8, 'd') и передать эти точные значения функции запуска. Я обновил свой вопрос, чтобы более четко отразить это намерение.
@Kitiara Как я и ожидал, поэтому я не использовал random
и т. д. в своем ответе. Обратите внимание, что ваш новый дополнительный вопрос полностью отличается от исходного вопроса. Не стесняйтесь создавать новый отдельный вопрос. Редактирование вашего старого вопроса для включения нового вопроса не рекомендуется, так как это сделает недействительными уже 2 опубликованных вопроса.
@user12002570 user12002570 Конечно, вот оно stackoverflow.com/questions/78808660/…
@Kitiara Я добавил туда ответ, в котором используется std::tuple
вместо std::unordered_map
.
Если вы хотите начать с нуля для каждой переменной, у меня есть простое решение. Если вы хотите, чтобы разные типы начинались с разных значений, все становится сложнее. Что хочешь?