С++ объединяет значения из двух (или более) пакетов параметров

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

LetsStart<int, -1, 10, 3>("This is -1", "And this is 10", "3!!!");

который будет выводить

-1 This is -1                                                                                                                                   
10 And this is 10                                                                                                                               
3 3!!! 

То есть он должен выбрать 1-е значение из первого пакета и 1-е значение из второго пакета, затем 2-е значение из обоих пакетов, затем 3-е значение из обоих пакетов и так далее...

В первых своих попытках я пробовал что-то вроде этого

template<class T, T... Firsts, class TSecond>
void LetsStart(TSecond... Seconds) {
   Output((Firsts, Seconds)...);
}

но это не сработало...

// Конечно, я написал эту функцию, но я считаю, что существует более правильный и простой способ выполнить эту задачу. Итак, не могли бы вы указать мне дорогу?


person Void    schedule 27.09.2014    source источник
comment
Идея состоит в том, чтобы хранить каждый пакет в кортеже (или другом классе), а затем перебирать эти два кортежа с помощью индекса N, который идет от 0 до sizeof ... (Firsts). Я бы начал на него отвечать, если бы не считал получившийся класс немного бесполезным... что вы этим имеете в виду?   -  person davidhigh    schedule 28.09.2014
comment
Я не очень понимаю требование, но почему вы не можете просто использовать карту?   -  person Zaid Amir    schedule 28.09.2014
comment
Вы на правильном пути; вы просто не можете расширять пакеты параметров в этом контексте. Одним из контекстов, в котором разрешено расширение, является список init-list в фигурных скобках: а/0c4980989603a770   -  person dyp    schedule 28.09.2014


Ответы (2)


В С++ 11 вы можете использовать следующую функцию:

template<typename T, T... V, typename F, typename... A>
void f(F &&f, A&&... args) {
    static_assert(sizeof...(V) == sizeof...(A), "Argument lists size mismatch");
    int arr[] = { 0, (std::forward<F>(f)(V, std::forward<A>(args)), 0)... };
    (void)arr;
}

Он принимает два списка параметров по запросу и дополнительную функцию F, затем распаковывает параметры и передает N-й из каждого списка заданной функции.
Это должно работать и в случае отсутствия предусмотрены параметры.

В С++ 17 вы можете переписать его следующим образом:

template<typename T, T... V, typename F, typename... A>
void f(F &&f, A&&... args) {
    static_assert(sizeof...(V) == sizeof...(A));
    (std::forward<F>(f)(V, std::forward<A>(args)), ...);
}

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

Вы можете протестировать две функции на примере main ниже:

#include<utility>
#include<iostream>

// ...

int main() {
    auto l = [](auto v, auto s) { std::cout << v << " " << s << std::endl; };
    f<int, -1, 10, 3>(l, "This is -1", "And this is 10", "3!!!");
}
person skypjack    schedule 09.09.2016
comment
Почему при звонке на f (std::forward<F>(f)(/*...*/)) требуется переадресация? - person Innokentiy Alaytsev; 05.02.2019
comment
@InnokentiyAlaytsev учитывать квалификаторы ссылок в операторе вызова функции, если таковые имеются. - person skypjack; 05.02.2019

Версия С++ 11:

CE: https://gcc.godbolt.org/z/xIUL1J

#include<iostream>

template<class... T>
void LetsStart(){}

template<class T, class U>
void print(T t, U u)
{
    std::cout << t << " " << u << '\n';
}

template<class T, T f,  T... ts, class A, class... Arg>
void LetsStart(A&& a, Arg&&... args){
    print(f, a);
    LetsStart<T, ts...>(args...);
}

void foo() {
    LetsStart<int, -1, 10, 3>("This is -1", "And this is 10", "3!!!");
}
person balki    schedule 05.02.2019
comment
Версия C++11, предоставленная @skypjack, имеет важное преимущество перед вашей реализацией — она не рекурсивна. В конце концов, это на самом деле не имеет значения, потому что компилятор все встроит. Но если кто-то хочет создать отладочную сборку, нерекурсивная версия приводит к гораздо меньшему количеству сборок и промежуточных функций. - person Innokentiy Alaytsev; 05.02.2019