C ++ parameter package with one type forced in arguments

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 do not satisfy: they just want to check that the parameter package has one type, but I want to convert the values โ€‹โ€‹into it into arguments (otherwise it does not work).

C ++ parameter package limited to instances of the same type?

Parameter with non-deduced type after the parameter package

Specifying the same type for all arguments passed to the function of the variational function or variational pattern w / out using an array, vector, structures, etc.?

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

What are my options?

+7
c ++ c ++ 11 c ++ 14 variadic-templates c ++ 17
source share
4 answers

Jarod42 slightly extended approach 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 that (as max66 pointed out) 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]

Another edit:

This one (as suggested by Jarod42) provides exactly the same syntax for the call as in the OP 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]

+4
source share

Well, if you can afford to change the syntax of the bits a 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({}, {}) become foo({{}, {}}) . Here is a working example: https://ideone.com/slbKi3

The problem with the way you want ( foo({}, {}) ) is that the compiler is not able to find out what it needs to convert {} to.

I tried to find a way to report this, but he did not listen at all.

+2
source share

If you agree, suggested by Telokis, add a parenthesis level that calls foo()

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

you can use the type C array trick suggested by Telokis and a simple loop to initialize the return 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 with C ++ 17, so the previous foo effectively constexpr , only starting with C ++ 17.

So you can call

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

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

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

only starting with C ++ 17.

+2
source share

One (limited) way to preserve syntax is to have multiple 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 }}; } 

WF in the answer shows how to generate it due to the variability in the class.

+1
source share

All Articles