10

I want to be able to do the following:

#include <array>
struct blah { };

template<typename... Args>
constexpr auto foo(Args&&... args)
{
    return std::array<blah, sizeof...(Args)>{{ args... }};
}

auto res = foo({}, {});

The following answers aren't satisfying: they just want to check that the parameter pack is of a single type, but I want to convert the values right to it in the arguments (else it does not work).

C++ parameter pack, constrained to have instances of a single type?

Parameter with non-deduced type after parameter pack

Specifying one type for all arguments passed to variadic function or variadic template function w/out using array, vector, structs, etc?

I also can't use initializer_list since I wouldn't be able to count the number of arguments to pass to the array type. And I especially don't want to type foo(blah{}, blah{});.

What are my possibilities ?

Jean-Michaël Celerier
  • 7,412
  • 3
  • 54
  • 75
  • 2
    For lazy people: https://ideone.com/bzjh00 – Telokis Nov 24 '17 at 10:18
  • (side-question: wasn't there ideone integration directly in stackoverflow at some point in time ? I seem to remember being able to test-run c++ code directly in answers but maybe I've just dreamed...) – Jean-Michaël Celerier Nov 24 '17 at 10:23
  • Never heard about that – Telokis Nov 24 '17 at 10:23
  • 2
    I'm not sure what you want is possible as is. The compiler can't deduce what `{}` is and I'm not sure there is a way to do so. But, as usual, some C++ guru will come and prove me wrong by submitting a 10 meta functions based answer. – Telokis Nov 24 '17 at 10:27

5 Answers5

5

A little bit expanded approach of Jarod42 for lazies (C++17):

#include <utility>
#include <array>

struct blah {};

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

template <class T, std::size_t N, class = std::make_index_sequence<N>>
struct bar_impl;

template <class T, std::size_t N, std::size_t... Is>
struct bar_impl<T, N, std::index_sequence<Is...>> {
    static auto foo(typer<T, Is>... ts) {
        return std::array<T, N>{{ts...}};
    }
};

template <class T = blah, std::size_t N = 10, class = std::make_index_sequence<N>>
struct bar;

template <class T, std::size_t N, std::size_t... Is>
struct bar<T, N, std::index_sequence<Is...>>: bar_impl<T, Is>... {
    using bar_impl<T, Is>::foo...;
};

int main() {
    bar<>::foo({}, {});
}

[live demo]

Edit:

Some C++14 solution which (as noted by max66) is even simpler than I expected:

#include <utility>
#include <array>

struct blah {};

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

template <class T = blah, std::size_t N = 10, class = std::make_index_sequence<N>>
struct bar;

template <class T, std::size_t N, std::size_t... Is>
struct bar<T, N, std::index_sequence<Is...>>: bar<T, N - 1> {
    using bar<T, N - 1>::foo;
    static auto foo(typer<T, Is>... ts) {
        return std::array<T, N>{{ts...}};
    }
};

template <class T>
struct bar<T, 0, std::index_sequence<>> {
    static auto foo() {
        return std::array<T, 0>{{}};
    }
};

int main() {
    bar<>::foo({}, {});
}

[live demo]

One more edit:

This one (as suggested by Jarod42) provides exactly the same syntax for invocation as in OP's question:

#include <utility>
#include <array>

struct blah {};

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

template <class T = blah, std::size_t N = 10, class = std::make_index_sequence<N>>
struct bar;

template <class T, std::size_t N, std::size_t... Is>
struct bar<T, N, std::index_sequence<Is...>>: bar<T, N - 1> {
    using bar<T, N - 1>::operator();
    auto operator()(typer<T, Is>... ts) {
        return std::array<T, N>{{ts...}};
    }
};

template <class T>
struct bar<T, 0, std::index_sequence<>> {
    auto operator()() {
        return std::array<T, 0>{{}};
    }
};

bar<> foo;

int main() {
    foo({}, {});
}

[live demo]

W.F.
  • 13,888
  • 2
  • 34
  • 81
  • 1
    If `bar` inherit recursively from `bar` and `foo()` is defined inside `bar`, you can avoid `bar_impl` and make this works also with C++14 (I suppose also C++11 with a surrogate for `std::index_sequence`) – max66 Nov 24 '17 at 13:15
  • awesome ! I honestly did not think that this was possible to do at all. – Jean-Michaël Celerier Nov 24 '17 at 15:53
2

Alright, if you can afford to change the syntax a little bit, this is the best I managed to find:

#include <array>

// to_array implementation taken from 
// http://en.cppreference.com/w/cpp/experimental/to_array
namespace detail {
template <class T, std::size_t N, std::size_t... I>
constexpr std::array<std::remove_cv_t<T>, N>
    to_array_impl(T (&a)[N], std::index_sequence<I...>)
{
    return { {a[I]...} };
}
}

template <class T, std::size_t N>
constexpr std::array<std::remove_cv_t<T>, N> to_array(T (&a)[N])
{
    return detail::to_array_impl(a, std::make_index_sequence<N>{});
}
// End of to_array implementation

struct blah { };

template<std::size_t N>
constexpr auto foo(const blah(&arr)[N])
{
    return to_array(arr);
}

int main()
{
    auto res = foo({{}, {}});
    return 0;
}

As you can see, foo({}, {}) became foo({{}, {}}). Here is a working example: https://ideone.com/slbKi3

The issue with the way you want it (foo({}, {})) is that the compiler has no way to know what it is supposed to convert {} to.

I tried to find a way to let it know but it didn't listen at all.

Telokis
  • 3,399
  • 14
  • 36
  • Nice. But I suppose can be simplified: if you call immediately `to_array_impl()` from `foo()` (you know `N` in `foo()` so you can call `std::make_index_sequence{}` from `foo()`) you can avoid the `to_array()` step. – max66 Nov 24 '17 at 11:00
2

If you accept, as proposed by Telokis, to add a bracket level calling foo()

auto res = foo( { {}, {} } );

you can use the C-style array trick proposed by Telokis and a simple cycle to initialize the returned value

template <std::size_t N>
constexpr std::array<blah, N> foo (const blah(&arr)[N])
 {
   std::array<blah, N> ret;

   for ( auto i = 0U ; i < N ; ++i )
      ret[i] = arr[i];

   return ret;
 }

Unfortunately the operator[] for std::array is constexpr only starting from C++17, so the preceding foo is effectively constexpr only starting from C++17.

So you can call

auto res = foo( { {}, {} } );

also in C++11 and C++14, but

constexpr auto res = foo( { {}, {} } );

only starting from C++17.

max66
  • 65,235
  • 10
  • 71
  • 111
2

One (limited) ways to keep your syntax is to have several overloads:

constexpr auto foo(const blah& a1)
{
    return std::array<blah, 1>{{ a1 }};
}

constexpr auto foo(const blah& a1, const blah& a2)
{
    return std::array<blah, 2>{{ a1, a2 }};
}

// ...

// Up to N
constexpr auto foo(const blah& a1, const blah& a2, .., const blah& aN)
{
    return std::array<blah, N>{{ a1, a2, .., aN }};
}

W.F. in his answer shows a way to generate it thanks to variadic at class scope.

Jarod42
  • 203,559
  • 14
  • 181
  • 302
2

In C++17 you can use a combination of static_assert and std::conjunction like this:

#include <array>
#include <type_traits>

struct blah {};

template <typename Arg, typename... Args>
constexpr auto foo_helper(Arg&& first, Args&&... rest) {
    
    static_assert(std::conjunction_v<std::is_same<Arg, Args>...>);
    
    return std::array<blah, 1 + sizeof...(Args)>{first, rest...};
}

template <typename... Args>
constexpr auto foo(Args&&... args) {
    return foo_helper(std::forward<Args>(args)...);
}

auto res = foo(blah{}, blah{})

Marco M.
  • 2,956
  • 2
  • 29
  • 22