So I was trying to come up with a way to split a given parameter pack args... into two separate packs args1... and args2... (at some specified index, 3 in this case). I also wanted to minimize the overhead as much as possible (I rather want to avoid solutions involving the instantiation of tuples and std::apply if that would even help).
Say sizeof...(Ts) == N+M. The solution I came up with was to first create an index sequence of length N, allowing us to have access to a pack I1... of indices 0, 1, ..., N-1. I also put the Ts... into a tuple type and aliased it with tuple_t. Then using a second lambda, I can make use of the index sequence by putting std::tuple_element_t<I1, tuple_t>&&... args1 as the first arguments, which forces the first N elements of args... to match with args1... and the remainder with args2.... From here I can now access args1..., args2... and Ts2.... Lastly I wrapped things into a third lambda in order to also be able to access Ts1....
#include <iostream>
template <typename... Ts>
constexpr void test(Ts&&... args) {}
template <typename... Ts>
void split_pack(Ts&&... args) {
using tuple_t = std::tuple<Ts...>;
[&]<size_t... I1>(std::index_sequence<I1...>) {
[&]<typename... Ts2>(std::tuple_element_t<I1, tuple_t>&&... args1, Ts2&&... args2) {
std::cout << "hi\n"; // why does MSVC not execute this?? But if I remove the call to test it does???
[&]<typename... Ts1>()
{
// now I have separate access to args1... and args2..., also Ts1... and Ts2...
test(std::forward<Ts1>(args1)...); // try doing something with args1...
}.template operator()<std::tuple_element_t<I1, tuple_t>...>();
}(std::forward<Ts>(args)...);
}(std::make_index_sequence<3>{});
}
int main() { split_pack(1, 3.0f, false, 5.0, 'a'); }
Now the problem I am facing is that while it compiles and runs just fine using Clang, it fails to compile using GCC and gives an unexpected result using MSVC.
GCC gives the following error:
<source>: In instantiation of 'split_pack<int, float, bool, double, char>(int&&, float&&, bool&&, double&&, char&&)::<lambda(std::index_sequence<_Inds ...>)>::<lambda(std::tuple_element_t<0, std::tuple<int, float, bool, double, char> >&&, std::tuple_element_t<1, std::tuple<int, float, bool, double, char> >&&, std::tuple_element_t<2, std::tuple<int, float, bool, double, char> >&&, Ts2&& ...)> [with Ts2 = {double, char}; std::tuple_element_t<0, std::tuple<int, float, bool, double, char> > = int; std::tuple_element_t<1, std::tuple<int, float, bool, double, char> > = float; std::tuple_element_t<2, std::tuple<int, float, bool, double, char> > = bool]':
<source>:10:9: required from 'split_pack<int, float, bool, double, char>(int&&, float&&, bool&&, double&&, char&&)::<lambda(std::index_sequence<_Inds ...>)> [with long unsigned int ...I1 = {0, 1, 2}; std::index_sequence<_Inds ...> = std::integer_sequence<long unsigned int, 0, 1, 2>]'
10 | [&]<typename... Ts2>(std::tuple_element_t<I1, tuple_t>&&... args1,
| ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
11 | Ts2&&... args2) {
| ~~~~~~~~~~~~~~~~~
12 | std::cout << "hi\n"; // why does MSVC not execute this?? But if I
| ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
13 | // remove the call to test it does???
| ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
14 | [&]<typename... Ts1>() {
| ~~~~~~~~~~~~~~~~~~~~~~~~
15 | // now I have separate access to args1... and args2..., also
| ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
16 | // Ts1... and Ts2...
| ~~~~~~~~~~~~~~~~~~~~
17 | test(std::forward<Ts1>(
| ~~~~~~~~~~~~~~~~~~~~~~~
18 | args1)...); // try doing something with args1...
| ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
19 | }.template operator()<std::tuple_element_t<I1, tuple_t>...>();
| ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
20 | }(std::forward<Ts>(args)...);
| ~
<source>:21:6: required from 'void split_pack(Ts&& ...) [with Ts = {int, float, bool, double, char}]'
9 | [&]<size_t... I1>(std::index_sequence<I1...>) {
| ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
10 | [&]<typename... Ts2>(std::tuple_element_t<I1, tuple_t>&&... args1,
| ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
11 | Ts2&&... args2) {
| ~~~~~~~~~~~~~~~~~
12 | std::cout << "hi\n"; // why does MSVC not execute this?? But if I
| ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
13 | // remove the call to test it does???
| ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
14 | [&]<typename... Ts1>() {
| ~~~~~~~~~~~~~~~~~~~~~~~~
15 | // now I have separate access to args1... and args2..., also
| ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
16 | // Ts1... and Ts2...
| ~~~~~~~~~~~~~~~~~~~~
17 | test(std::forward<Ts1>(
| ~~~~~~~~~~~~~~~~~~~~~~~
18 | args1)...); // try doing something with args1...
| ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
19 | }.template operator()<std::tuple_element_t<I1, tuple_t>...>();
| ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
20 | }(std::forward<Ts>(args)...);
| ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
21 | }(std::make_index_sequence<3>{});
| ~^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
<source>:24:24: required from here
24 | int main() { split_pack(1, 3.0f, false, 5.0, 'a'); }
| ~~~~~~~~~~^~~~~~~~~~~~~~~~~~~~~~~~~~
<source>:17:21: error: 'args1#0' is not captured
17 | test(std::forward<Ts1>(
| ~~~~^~~~~~~~~~~~~~~~~~~
18 | args1)...); // try doing something with args1...
| ~~~~~~~~~~
<source>:17:21: note: 'std::tuple_element_t<0, std::tuple<int, float, bool, double, char> >& args1#0' declared here
<source>:17:21: error: 'args1#1' is not captured
<source>:17:21: note: 'std::tuple_element_t<1, std::tuple<int, float, bool, double, char> >& args1#1' declared here
<source>:17:21: error: 'args1#2' is not captured
<source>:17:21: note: 'std::tuple_element_t<2, std::tuple<int, float, bool, double, char> >& args1#2' declared here
Compiler returned: 1
It tells me that args1... is not captured, but clearly I used implicit captures & everywhere?
Trying to run it on MSVC, it does not want to execute the std::cout statement for some reason, but if I remove the call to test, it now executes the std::cout just fine?
So I am wondering if there are any mistakes in this code, or if this somehow triggered two different compiler bugs in two different compilers.
auto lambda1 = []() { ... }). If that doesn't help rewrite them as actual functions. Call the functions one by one, storing their result in a variable, passed to the next function. Etc. Worst case, throw away what you have and rewrite it, without lambdas and templates and functions. Write and build one single line at a time, as simple and straightforward as possible.index_sequenceDemostd::tuple_element_t<I1, tuple_t>&&is no longer a forwarding-reference, but a r-value-reference.