Unpack variadic template to initializer_list and call two functions at once
Asked Answered
P

3

5

I have two vectors:

std::vector<int> v1{ 1, 2, 3 };
std::vector<int> v2{ 4, 5, 6 };

I want to create an object of std::initializer_list which holds iterators to the first and last elements of above vectors.

I want to have a function with a variadic template:

template<class... Ts>
void foo(Ts... args)

and inside above function I want to unpack all arguments. So far I implemented:

template<class... Ts>
void foo(Ts... args)
{
  std::initializer_list<std::vector<int>::iterator> il{
    (std::begin(args), std::end(args))...
  };
}

int main()
{
  std::vector<int> v1{ 1, 2, 3 };
  std::vector<int> v2{ 4, 5, 6 };

  foo(v1, v2);
}

but it doesn't work as expected due to operator,. Current implementation creates initializer_list with two iterators. What I want in this case is to have an initializer_list with 4 iterators pointing to the first and one past the end element of these two vectors. I want it to be begin, end, begin, end.

Program answered 10/7, 2023 at 11:15 Comment(4)
Do you need them to be interleaved, begin, end, begin, end - or would begin, begin, end, end work?Cheap
begin, end, begin, end.Program
BTW, your signature is strange, Ts would be any type whereas you only expect std::vector<int>, taking std::initializer_list<std::reference_wrapper<std::vector<int>>> might be more appropriate.Godred
The problem is more complicated than you think. For simplicity of the question I modified my source code. The answers I received resolve my problem.Program
P
4

You can make a new pack which is double the length:

#include <vector>
#include <utility>
#include <tuple>

namespace detail {

template<std::size_t... I, class... Ts>
void foo_impl(std::index_sequence<I...>, Ts&... args) {
    std::initializer_list<std::vector<int>::iterator> il{
        [&]() -> std::vector<int>::iterator {
            auto& vec = std::get<I/2u>(std::tie(args...));
            return I % 2u == 0u ? std::begin(vec) : std::end(vec);
        }()...
    };

    // ...
}

}

template<class... Ts>
void foo(Ts... args) {
    return detail::foo_impl(std::make_index_sequence<sizeof...(args)*2u>{}, args...);
}

Or consider if you need an initializer_list at all. You might be able to use an array:

template<class... Ts>
void foo(Ts... args) {
    std::vector<int>::iterator il[sizeof...(args)*2u];
    {
        auto* p = std::begin(il);
        (((*p++ = std::begin(args)), (*p++ = std::end(args))), ...);
    }

    // ...
}
Pyrogen answered 10/7, 2023 at 11:51 Comment(0)
G
6

You might create array instead of initializer_list:

template<class... Ts>
auto foo(Ts&... args)
{
    std::array<typename std::common_type_t<std::decay_t<Ts>...>::iterator, 2 * sizeof...(Ts)> res;

    std::size_t i = 0;
    ((res[i++] = args.begin(), res[i++] = args.end()), ...);

    return res;
}

Demo

Godred answered 10/7, 2023 at 11:51 Comment(0)
C
4

I think your best bet is to add the iterators in forms of std::pairs:

template<class... Ts>
void foo(Ts... args) {
    std::initializer_list<std::pair<std::vector<int>::iterator,
                                    std::vector<int>::iterator>> il
    {
        {std::begin(args), std::end(args)}...
    };
}

This initializer_list would now have two std::pairs with begin() and end() iterators, that is, 4 iterators in total.

Cheap answered 10/7, 2023 at 11:27 Comment(2)
That is a great answer, but unfortunately I cannot have std::pair in my std::initializer_list. Is it possible to do it without nested types inside std::initializer_list?Program
@RagdollCar Hmm, I will need to give that a think :-)Cheap
P
4

You can make a new pack which is double the length:

#include <vector>
#include <utility>
#include <tuple>

namespace detail {

template<std::size_t... I, class... Ts>
void foo_impl(std::index_sequence<I...>, Ts&... args) {
    std::initializer_list<std::vector<int>::iterator> il{
        [&]() -> std::vector<int>::iterator {
            auto& vec = std::get<I/2u>(std::tie(args...));
            return I % 2u == 0u ? std::begin(vec) : std::end(vec);
        }()...
    };

    // ...
}

}

template<class... Ts>
void foo(Ts... args) {
    return detail::foo_impl(std::make_index_sequence<sizeof...(args)*2u>{}, args...);
}

Or consider if you need an initializer_list at all. You might be able to use an array:

template<class... Ts>
void foo(Ts... args) {
    std::vector<int>::iterator il[sizeof...(args)*2u];
    {
        auto* p = std::begin(il);
        (((*p++ = std::begin(args)), (*p++ = std::end(args))), ...);
    }

    // ...
}
Pyrogen answered 10/7, 2023 at 11:51 Comment(0)

© 2022 - 2025 — McMap. All rights reserved.