Обработка нескольких вхождений одного и того же типа в пакете параметров шаблона с переменным числом вариантов

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

У меня есть следующая функция шаблона с переменным числом аргументов, которая принимает несколько аргументов и печатает их:

#include <iostream>

template<typename... Args>
void run(Args... var)
{
    (std::cout << ... << var) << std::endl;
}

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

  1. Обрабатывает несколько вхождений одного и того же типа в пакете параметров шаблона.
  2. Обрабатывает эти аргументы и присваивает им определенные значения.
  3. Передает собранные аргументы функции запуска.

Например, если я вызываю процессArgs<int, double, int, int, double>(), я хочу, чтобы он:

  1. Создайте целочисленные переменные, двойные переменные и т. д. для каждого типа в пакете параметров.
  2. Присвойте этим переменным определенные значения (например, увеличивая значения для каждого экземпляра одного и того же типа).
  3. Передайте эти переменные в функцию run, чтобы она вела себя так, как если бы была вызвана run(42, 3.14, 43, 44, 4.14).

Вот желаемый результат:

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 28.07.2024 06:34

@NathanOliver Различные типы начинаются с разных значений, все случайны

Kitiara 28.07.2024 06:37

@Kitiara На самом деле это может быть не так уж и плохо реализовать. Посмотрим, что получится. Возможно, через некоторое время мне будет с чего начать. Есть ли диапазон случайных значений, которые вам нужны для каждого типа? Я предполагаю, что не до максимума, учитывая, что вы хотите каждый раз увеличивать его на единицу.

NathanOliver 28.07.2024 06:39

@NathanOliver Да, макса нет.

Kitiara 28.07.2024 06:44

Создайте одну новую структуру с вашими аргументами в качестве членов и используйте ее в качестве аргумента шаблона.

Pepijn Kramer 28.07.2024 07:23

Ваша главная цель — передать аргумент нужным вам способом, а не создавать случайные значения, верно? Судя по вашей текущей формулировке, вам на самом деле нужны были не «случайные» значения, а значения, которые были установлены пользователем, и ваша главная задача заключалась в том, чтобы найти способ передавать и обрабатывать аргументы определенным образом, как вы сказали: «Я не мне все равно, какие цифры, "

Alan 28.07.2024 09:04

@Алан Да, это правильно. Значения были просто примером, возможно, вопрос был не таким информативным. Предоставленные решения действительно являются решениями для примера в вопросе, однако они не решают мою главную проблему.

Kitiara 28.07.2024 18:58

@Алан, я изменил описание проблемы и пример результата. Теперь это намного информативнее.

Kitiara 28.07.2024 19:08
Стоит ли изучать 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 называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
2
8
86
2
Перейти к ответу Данный вопрос помечен как решенный

Ответы 2

Новый ответ на основе обновления вопроса:

По сути, у вас будет та же форма шаблона для 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 28.07.2024 19:11

@Kitiara Я добавил обновление, которое должно делать то, что вы просили в своем обновлении.

NathanOliver 28.07.2024 19:20

Да, это работает для случая (std::cout << ... << var) << std::endl;, но это не работает, если мы держим std::function векторов, где каждая функция требует разных параметров.

Kitiara 28.07.2024 22:43

@Kitiara Можете ли вы привести пример?

NathanOliver 28.07.2024 23:01

@Kitiara Обратите внимание, что вам не следует редактировать свой вопрос так, чтобы это меняло всю суть вопроса, если уже опубликовано 2 ответа. Не стесняйтесь задавать новый отдельный вопрос для вашего вопроса с другим намерением. Как правило, если новый дополнительный вопрос существенно отличается от исходного вопроса, следует задать новый вопрос, чтобы уже опубликованные ответы не были признаны недействительными.

user12002570 29.07.2024 05:57
Ответ принят как подходящий

Вы можете использовать шаблон класса и член данных 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 28.07.2024 07:32

@NathanOliver Это работает в рабочей демо. Я посмотрю, что говорит стандарт, и, возможно, задам новый вопрос.

user12002570 28.07.2024 07:32

Ага, здесь нет: coliru.stacked-crooked.com/a/4d9ec7c5ccd25754. ленивый

NathanOliver 28.07.2024 07:33

@NathanOliver Я создам новый вопрос. Тем временем я отредактирую этот ответ, чтобы специализировать весь класс.

user12002570 28.07.2024 07:35

Похоже, что GCC и MSVC ошибаются, clang компилируется.

NathanOliver 28.07.2024 07:36

@NathanOliver Ошибка возникает только тогда, когда мы используем inline со статическим элементом данных. Если мы используем старый способ определения вне класса, тогда gcc и msvc также начнут принимать программу. Демо

user12002570 28.07.2024 07:57

@user12002570 user12002570 Спасибо за решение моего предыдущего вопроса. Я понял, что моя главная цель не была четко сформулирована. Вместо создания случайных значений моя цель — передать конкретные значения в функцию шаблона с вариационными значениями, но это пошло не так. Например, я хочу вызватьprocessArgs("text", 4.6, 'c', 4, "more text", 7.8, 'd') и передать эти точные значения функции запуска. Я обновил свой вопрос, чтобы более четко отразить это намерение.

Kitiara 28.07.2024 19:10

@Kitiara Как я и ожидал, поэтому я не использовал random и т. д. в своем ответе. Обратите внимание, что ваш новый дополнительный вопрос полностью отличается от исходного вопроса. Не стесняйтесь создавать новый отдельный вопрос. Редактирование вашего старого вопроса для включения нового вопроса не рекомендуется, так как это сделает недействительными уже 2 опубликованных вопроса.

user12002570 29.07.2024 06:00

@user12002570 user12002570 Конечно, вот оно stackoverflow.com/questions/78808660/…

Kitiara 29.07.2024 20:56

@Kitiara Я добавил туда ответ, в котором используется std::tuple вместо std::unordered_map.

user12002570 30.07.2024 07:21

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

Похожие вопросы