0

Here's an example usage of a parameter pack:

template <typename... T>
bool foo(A x) {
  return (baz<T>(x) || ...);
}

I'd like to do something similar, but with pairs of types. Here's an example (note that this isn't real C++ syntax):

template <typename... [T, Z]>
bool foo(A x) {
  return (baz<T, Z>(x) || ...);
}

How is it possible to do something like this (i.e, a parameter pack over pairs)?

Here is a larger fleshed example of what I intend to do:

// caller
foo<{A1, A2}, {B1, B2}, C, D>(x);
// callee should expand to...
int foo(void* x) {
  if (baz<A1,A2>(x) || baz<B1, B2>(x)) return 0;
  if (bar<C>(x) || bar<D>(x)) return 1;
  return 2
}
7
  • 3
    Pack them in a tuple (or something similar)?
    – Jarod42
    Commented Jul 9 at 1:15
  • Use the old-fashioned recursion instead of those newfangled fold expressions. Commented Jul 9 at 4:14
  • Always post driver/calling code also. From current question it is not clear if by pair you mean std::pair or just informal term pair. For the latter you'll just have to introduce another parameter pack after T. Like template <typename... T, typename... Z> Note that though the latter syntax is not allowed in c++. Commented Jul 9 at 4:19
  • How would I do the latter @user12002570 ? I'm using it in the informal sense, where a pair of "types" would have to be supplied together and are closely related. I had thought you cannot have two parameter packs for the same function. Commented Jul 9 at 5:21
  • @user129393192 Are you aware of std::pair? If yes, you could use it to show the calling code. Just to give an idea of how you intend on using it. Just like you gave an example pseudocode for the modified foo. Commented Jul 9 at 5:24

1 Answer 1

2

You might use parameter to pack the pairs, something like:

template <typename... Ts, typename... Us>
bool foo(std::tuple<std::pair<Ts, Us>...>, A x) {
  return (baz<Ts, Us>(x) || ...);
}

Demo

Possibly wrap with std::type_identity (to avoid to construct the type), or create your own wrapper (type_list<Ts...>).

From your edit, you mix pair with non pair types, so use regular variadic template and dispatch according to the inner type:

// dispatcher helper
template <typename... Ts>
struct overloaded : Ts...
{
    using Ts::operator()...;
};

template <typename... Ts>
bool foo(A a) {
    auto func = overloaded{
        []<typename T>(std::type_identity<T>, A a){ return baz<T>(a); },
        []<typename T1, typename T2>(std::type_identity<std::pair<T1, T2>>, A a){ return baz<T1, T2>(a); },
        []<typename... Us>(std::type_identity<std::tuple<Us...>>, A a){ return baz<Us...>(a); }
    };
    
    return (func(std::type_identity<Ts>{}, a) || ...);
}

Usage similar to:

foo<
    std::pair<int, float>,
    std::pair<int, int>,
    char,
    std::tuple<char, int, float>,
    float>(A{});

Demo

4
  • Is there a way to do this without having extra function parameters? For example, so instead of func(std::type_identity<Ts>{}, ... you could do func<Ts>(...). Commented Jul 10 at 18:51
  • It is the conversion of std::pair<T1, T2> to baz<T1, T2> which requires that indirection, if baz<std::pair<T1, T2>> works as wanted, a simple (baz<Ts>(a) || ...) would do the job.
    – Jarod42
    Commented Jul 11 at 10:52
  • Couldn't you use T::first_type or T::second_type? I know there is a way by using std::enable_if but I'm curious if perhaps there is a cleaner method similar to what you've done with the overloading. Commented Jul 13 at 5:06
  • Issue is that T might have several form, you have to dispatch in one way or other, overload is a simple way, if constexpr might be another (that one would probably need extra traits to check if type is a std::pair or not.
    – Jarod42
    Commented Jul 13 at 11:14

Not the answer you're looking for? Browse other questions tagged or ask your own question.