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 ?
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]
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.
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.
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.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With