0

I would like to do the following but didn't know why it didn't compile in C++17.

int func(int a) {
  // do some operations on a
  int res = operation_on_a(a);
  return res;
}

auto funcs(int... as) {
  return make_tuple(func(as)...);
}

auto v1 = func(1);
auto [v1, v2, v3] = funcs(1, 2, 3);  // Why this doesn't compile?

Basically, the idea is to have a base function called func() and another function taking variadic arguments applying each item to the func() and return a std::tuple of the results. However, it doesn't get compiled unless I replaced int... with a template type. In this case I know arguments are all integers.

max66
  • 65,235
  • 10
  • 71
  • 111
Jes
  • 2,614
  • 4
  • 25
  • 45

1 Answers1

2

I would like to do the following but didn't know why it didn't compile in C++17.

Simply because

auto funcs(int... as) {
  return make_tuple(func(as)...);
}

isn't C++ syntax.

I think that

template <typename Args>
auto funcs (Args ... as)
 { return std::make_tuple(func(as)...); }

taking in count that every as... call func() that return an int, is a reasonable solution.

But if you really want somethig that is like a variadic function that accept a variadic number of int (and if you can set an upper limit for the number of arguments), I propose the following solution.

First of all, you need a template that receive a type and a std::size_t and return the type; something like

template <typename T, std::size_t>
using typer = T;

Now an helper recursive struct with self-inheritance

template <typename>
struct bar;

template <>
struct bar<std::index_sequence<>>
 { 
   static void f () {}
 };

template <std::size_t ... Is>
struct bar<std::index_sequence<Is...>>
   : public bar<std::make_index_sequence<sizeof...(Is)-1U>>
 {
   using bar<std::make_index_sequence<sizeof...(Is)-1U>>::f;

   static auto f (typer<int, Is>... as)
    { return std::make_tuple(func(as)...); }
 };

that define a sequence of bar structs with a static method f that receive some int (typer<int, Is> is int).

Now a struct foo

template <std::size_t N = 64U>
struct foo : public bar<std::make_index_sequence<N>>
 { };

that inherit from a sequence of bar structs and inherit a sequence of f() that receive zero, 1, 2, ..., N-1 ints.

Now you can call

auto [v1, v2, v3] = foo<>::f(1, 2, 3);

because in foo struct there is (also) a f() method that receive exactly three ints.

The following is a full working example

#include <tuple>
#include <iostream>
#include <type_traits>

int func(int a)
 { return a+1; }

template <typename T, std::size_t>
using typer = T;

template <typename>
struct bar;

template <>
struct bar<std::index_sequence<>>
 { 
   static void f () {}
 };

template <std::size_t ... Is>
struct bar<std::index_sequence<Is...>>
   : public bar<std::make_index_sequence<sizeof...(Is)-1U>>
 {
   using bar<std::make_index_sequence<sizeof...(Is)-1U>>::f;

   static auto f (typer<int, Is>... as)
    { return std::make_tuple(func(as)...); }
 };

template <std::size_t N = 64U>
struct foo : public bar<std::make_index_sequence<N>>
 { };

int main ()
 {    
   auto [v1, v2, v3] = foo<>::f(1, 2, 3);

   std::cout << v1 << ", " << v2 << ", " << v3 << std::endl;
 }
max66
  • 65,235
  • 10
  • 71
  • 111