Piecewise initialisation of std::array - c++

How can I write an std::array concatenation function?
template <typename T, std::size_t sza, std::size_t szb>
std::array<T, sza+szb> concat (const std::array<T, sza>& aa,
const std::array<T, szb>& ab)
{
std::array<T, sza+szb> result;
std::copy(std::begin(aa), std::end(aa), std::begin(result));
std::copy(std::begin(ab), std::end(ab), std::begin(result) + sza);
return result;
}
This of course doesn't work when T is not default-constructible. How can this be fixed?

Convert the parameters to parameter-pack expansions with std::index_sequence and a helper lambda.
template <typename T, std::size_t sza, std::size_t szb>
std::array<T, sza+szb> concat (const std::array<T, sza>& aa,
const std::array<T, szb>& ab)
{
auto doit = [&]<std::size_t... ai, std::size_t... bi>
(std::index_sequence<ai...>, std::index_sequence<bi...>)
{
return std::array<T, sza+szb>{aa[ai]..., ab[bi]...};
};
return doit(std::make_index_sequence<sza>{}, std::make_index_sequence<szb>{});
}

Explicit template parameter list for lambdas, as shown in n. m.'s answer, were introduced in C++20.
A C++14 solution needs an helper function:
template <typename T, std::size_t... ai, std::size_t... bi>
std::array<T, sizeof...(ai) + sizeof...(bi)>
concat_impl(std::array<T, sizeof...(ai)> const& aa,
std::array<T, sizeof...(bi)> const& ab,
std::index_sequence<ai...>, std::index_sequence<bi...>)
{
return std::array<T, sizeof...(ai) + sizeof...(bi)>{
aa[ai]..., ab[bi]...
};
};
template <typename T, std::size_t sza, std::size_t szb>
std::array<T, sza + szb> concat (std::array<T, sza> const& aa,
std::array<T, szb> const& ab)
{
return concat_impl(aa, ab,
std::make_index_sequence<sza>{},
std::make_index_sequence<szb>{});
}

With the help of std::tuple_cat and std::apply, you can
template <typename T, std::size_t sza, std::size_t szb>
std::array<T, sza+szb> concat(const std::array<T, sza>& aa,
const std::array<T, szb>& ab)
{
return std::apply([](auto... elems) { return std::array{elems...}; },
std::tuple_cat(aa, ab));
}

Related

How to write an overload function for std::array that calls a variadic function?

I have the following variadic function:
template <typename... Args>
CustomType<Args...> method(const Args& args...);
which works fine when I just do e.g.
method(1.0f, 2.0f, 3.0f);
However I also want to write an overload for std::array<T, N>:
template <typename T, std::size_t N>
auto method(const std::array<T, N>& arr);
Reading this post I figured I would use the following helper functions:
template <typename T, std::size_t N, typename VariadicFunc, uint32_t... I>
auto methodApply(const std::array<T, N>& arr, VariadicFunc func, std::index_sequence<I...>)
{
return func(arr[I]...);
}
template <typename T, std::size_t N, typename VariadicFunc, typename Indices = std::make_index_sequence<N>>
auto methodArr(const std::array<T, N>& arr, VariadicFunc func)
{
return methodApply(arr, func, Indices());
}
and then do
template <typename T, std::size_t N>
auto method(const std::array<T, N>& arr)
{
methodArr(arr, /* what to pass here? */);
}
Note: the reason why I would like to template on func is because I want these helper functions to be generic so that I may use them for other variadic functions, not just method().
Only I'm not sure what pass as the second argument - I was thinking of a lambda function that would make a call to the original function method(const Args& args...) but it's unclear to me how to do this.
EDIT:
Restricted to C++14.
You can wrap it in a lambda and let the compiler deduce the type for you
template <typename T, std::size_t N>
auto method(const std::array<T, N>& arr)
{
return methodArr(arr, [](const auto&... args) { return method(args...); });
}
Demo
In C++17, methodApply can be replaced with std::apply
template <typename T, std::size_t N>
auto method(const std::array<T, N>& arr) {
return std::apply(
[](const auto&... args) { return method(args...); }, arr);
}

constexpr variadic template and unpacking std::array

I'd like to write a constexpr template function that permutes elements of an array passed in as a parameter. So I've come up with something like this:
template <typename T, std::size_t N, typename... Ts>
constexpr std::array<T, N> permute(const std::array<T, N>& arr, const std::array<int, N>& permutation, Ts&&... processed)
{
return (sizeof...(Ts) == N) ?
std::array<T, N>{ std::forward<Ts>(processed)... } :
permute(arr, permutation, std::forward<Ts>(processed)..., arr[permutation[sizeof...(Ts)]]);
}
Usage example:
constexpr std::array<int, 3> arr{ 1, 2, 3 };
constexpr std::array<int, 3> permutation{ 2, 1, 0 };
constexpr auto result = permute(arr, permutation); //result should contain { 3, 2, 1 }
The problem is the above code doesn't compile. For some reason g++ 6.4 tries to instantiate the permute template with 4 and more parameters hidden under 'processed' template parameter pack.
Can you help me correct my code and make it compile?
Full code
I'll present a "quick fix" that demonstrates the cause of the problem, then show how to solve the problem in C++11. After that, I'll show how to use newer features (C++14 onward) to get a simpler implementation.
Diagnosis
The cause of your runaway compilation is that the compiler has to generate both branches of the conditional and check them for correctness, even though it could prove that one of them will never be evaluated.
In newer versions of C++, we can make it work by replacing the ? with an if constexpr:
#include <array>
#include <cstddef>
#include <utility>
template <typename T, std::size_t N, typename... Ts>
constexpr std::array<T, N> permute(const std::array<T, N>& arr,
const std::array<int, N>& permutation,
Ts&&... processed)
{
if constexpr (sizeof...(Ts) == N)
return std::array<T, N>{ std::forward<Ts>(processed)... };
else
return permute(arr, permutation, std::forward<Ts>(processed)...,
arr[permutation[sizeof...(Ts)]]);
}
int main()
{
constexpr std::array<int, 3> arr{ 1, 2, 3 };
constexpr std::array<int, 3> permutation{ 2, 1, 0 };
constexpr auto result = permute(arr, permutation);
return result != std::array<int, 3>{ 3, 2, 1 };
}
(For these newer versions of C++, this can be simplified further using std::index_sequence, as I'll show later).
C++11 code
C++11 doesn't have if constexpr, so we'll need to revert to SFINAE instead:
#include <array>
#include <cstddef>
#include <utility>
template <typename T, std::size_t N, typename... Ts>
constexpr typename std::enable_if<sizeof...(Ts) == N, std::array<T, N> >::type
permute(const std::array<T, N>&, const std::array<int, N>&,
Ts&&... processed)
{
return std::array<T, N>{ std::forward<Ts>(processed)... };
}
template <typename T, std::size_t N, typename... Ts>
constexpr typename std::enable_if<sizeof...(Ts) != N, std::array<T, N> >::type
permute(const std::array<T, N>& arr, const std::array<int, N>& permutation,
Ts&&... processed)
{
return permute(arr, permutation, std::forward<Ts>(processed)...,
arr[permutation[sizeof...(Ts)]]);
}
Here, we provide completely separate functions for sizeof...(Ts) == N and sizeof...(Ts) != N, and use std::enable_if to select between them.
C++14 onwards
If we're able to use C++14 or later, we get std::index_sequence, which greatly simplifies operating on all the elements of an array or tuple. This still requires two functions, but this time one of them calls the other, and the logic is a bit easier to follow:
#include <array>
#include <cstddef>
#include <utility>
template<typename T, std::size_t N, std::size_t... I>
constexpr std::array<T, N>
permute_impl(const std::array<T, N>& a, const std::array<int, N>& p,
std::index_sequence<I...>)
{
return { a[p[I]]... };
}
template<typename T, std::size_t N, typename I = std::make_index_sequence<N>>
constexpr std::array<T, N>
permute(const std::array<T, N>& a, const std::array<int, N>& p)
{
return permute_impl(a, p, I{});
}
It might even be worth implementing your own index_sequence if you need this more than once and you're constrained to using only C++11.

Multi patterend varadic templates in C++

I don't think this is possible based on what I've read however I'm hoping someone here may know of some solution that would get this to work.
I have a vector (maths) class for C++
template <typename T, size_t N> class vec;
And want to create a varadic friend function apply to apply a function to these vectors element-wise
i.e.
template <typename F, typename ...Args> friend vec<typename std::result_of<pow(Args&&...)>::type, N> apply(F&& f, const vec<Args, N>&... args);
which is valid (untested yet)
however I want to achieve a pattern like
template <typename F> friend vec<typename std::result_of<F&&(T&&)>::type, N> apply(F&& f, const vec<T, N>& V);
template <typename F> friend vec<typename std::result_of<F&&(T&&, T&&)>::type, N> apply(F&& f, const vec<T, N>& V1, const vec<T, N>& V2);
template <typename F> friend vec<typename std::result_of<F&&(T&&, T&&)>::type, N> apply(F&& f, const vec<T, N>& V1, const T& V2);
template <typename F> friend vec<typename std::result_of<F&&(T&&, T&&)>::type, N> apply(F&& f, const T& V1, const vec<T, N>& V2);
template <typename F, typename U> friend vec<typename std::result_of<F&&(T&&, U&&)>::type, N> apply(F&& f, const vec<T, N>& V1, const vec<U, N>& V2);
template <typename F, typename U> friend vec<typename std::result_of<F&&(T&&, U&&)>::type, N> apply(F&& f, const vec<T, N>& V1, const U& V2);
template <typename F, typename U> friend vec<typename std::result_of<F&&(U&&, T&&)>::type, N> apply(F&& f, const vec<U, N>& V1, const vec<T, N>& V2);
template <typename F, typename U> friend vec<typename std::result_of<F&&(U&&, T&&)>::type, N> apply(F&& f, const U& V1, const vec<T, N>& V2);
note that only one of the arguments is required to be a vector any scalars would be broadcasted to the length of the vector.
The idea is that apply(pow, /*vec<float,N>*/V, /*int*/n) -> {pow(V.v[i],n)...} where i -> 0 ... N rather than apply(pow, /*vec<float,N>*/V, /*int*/n) -> apply(pow, /*vec<float,N>*/V, /*vec<int,N>*/tmp{/*int*/n}) {pow(V.v[i],tmp.v[i])...}
So I would like to be able to write something like the following (which isn't valid C++, but it should give an idea of what I want to achieve)
template <typename F, typename ...Args> friend vec<typename std::result_of<pow(Args&&...)>::type, N> apply(F&& f, const vec<Args, N>&||scalar<Args>::type... args) {
vec<typename std::result_of<pow(Args&&...)>::type, N> r;
for (int i= 0; i < N; i++) { r = f((is_vec<Args>?args.v[i]:args)...); }
return r;
}
EDIT:
Based on Frank's comments I'm looking for something along the lines of
template<typename F, typename ...Args, size_t N>
vec<typename std::enable_if<sum<is_vec<Args,N>...>::value > 0, std::result_of<F&&(base_type<Args>::type&&...)>::type>::type, N>
(F&& f, Args&&...args) {
vec<typename std::result_of<F&&(base_type<Args>::type&&...)>::type, N> result;
for(std::size_t i = 0 ; i < N ; ++i) { result.v[i] = f(extract_v(std::forward<Args>(args),i)...); }
return result;
}
however I'm unsure if this version could even compile as it may be too ambiguous to be able to detriment the value of N.
Not sure to understand what do you exactly want but...
It seems to me that can be useful a custom type traits to extract, from a list of types, the dimension of the Vec, iff (if and only if) in the list of types there is at least one Vec and there aren't Vec's of different lengths.
I suggest something as follows, heavily based on template specialization,
template <std::size_t, typename ...>
struct dimVec;
// ground case for no Vecs: unimplemented for SFINAE failure !
template <>
struct dimVec<0U>;
// ground case with one or more Vecs: size fixed
template <std::size_t N>
struct dimVec<N> : public std::integral_constant<std::size_t, N>
{ };
// first Vec: size detected
template <std::size_t N, typename T, typename ... Ts>
struct dimVec<0U, Vec<T, N>, Ts...> : public dimVec<N, Ts...>
{ };
// another Vec of same size: continue
template <std::size_t N, typename T, typename ... Ts>
struct dimVec<N, Vec<T, N>, Ts...> : public dimVec<N, Ts...>
{ };
// another Vec of different size: unimplemented for SFINAE failure !
template <std::size_t N1, std::size_t N2, typename T, typename ... Ts>
struct dimVec<N1, Vec<T, N2>, Ts...>;
// a not-Vec type: continue
template <std::size_t N, typename T, typename ... Ts>
struct dimVec<N, T, Ts...> : public dimVec<N, Ts...>
{ };
with the help of a template static variable
template <typename ... Args>
static constexpr auto dimVecV { dimVec<0U, Args...>::value };
Now should be easy.
You can write an apply() function that receive a variadic list of args of types Args... and is SFINAE enabled iff dimVecV<Args...> is defined
template <typename F, typename ... Args, std::size_t N = dimVecV<Args...>>
auto apply (F && f, Args ... as)
{ return applyH1(std::make_index_sequence<N>{}, f, as...); }
Observe that the N variable is used to SFINAE enable/disable the function but is useful itself: it's used to pass a std::index_sequence from 0 to N-1 to the first helper function applyH1()
template <std::size_t ... Is, typename F, typename ... Args>
auto applyH1 (std::index_sequence<Is...> const &, F && f, Args ... as)
-> Vec<decltype(applyH2<0U>(f, as...)), sizeof...(Is)>
{ return { applyH2<Is>(f, as...)... }; }
that initialize the returned Vec with single values calculated from the second helper function applyH2()
template <std::size_t I, typename F, typename ... Args>
auto applyH2 (F && f, Args ... as)
{ return f(extrV<I>(as)...); }
that uses a set of template functions extrV()
template <std::size_t I, typename T, std::size_t N>
constexpr auto extrV (Vec<T, N> const & v)
{ return v[I]; }
template <std::size_t I, typename T>
constexpr auto extrV (T const & v)
{ return v; }
to extract the I-th element from a Vec or to pass-through a scalar value.
It's a little long but not particularly complicated.
The following is a full working example
#include <array>
#include <iostream>
#include <type_traits>
template <typename T, std::size_t N>
class Vec;
template <std::size_t, typename ...>
struct dimVec;
// ground case for no Vecs: unimplemented for SFINAE failure !
template <>
struct dimVec<0U>;
// ground case with one or more Vecs: size fixed
template <std::size_t N>
struct dimVec<N> : public std::integral_constant<std::size_t, N>
{ };
// first Vec: size detected
template <std::size_t N, typename T, typename ... Ts>
struct dimVec<0U, Vec<T, N>, Ts...> : public dimVec<N, Ts...>
{ };
// another Vec of same size: continue
template <std::size_t N, typename T, typename ... Ts>
struct dimVec<N, Vec<T, N>, Ts...> : public dimVec<N, Ts...>
{ };
// another Vec of different size: unimplemented for SFINAE failure !
template <std::size_t N1, std::size_t N2, typename T, typename ... Ts>
struct dimVec<N1, Vec<T, N2>, Ts...>;
// a not-Vec type: continue
template <std::size_t N, typename T, typename ... Ts>
struct dimVec<N, T, Ts...> : public dimVec<N, Ts...>
{ };
template <typename ... Args>
static constexpr auto dimVecV { dimVec<0U, Args...>::value };
template <std::size_t I, typename T, std::size_t N>
constexpr auto extrV (Vec<T, N> const & v)
{ return v[I]; }
template <std::size_t I, typename T>
constexpr auto extrV (T const & v)
{ return v; }
template <typename T, std::size_t N>
class Vec
{
private:
std::array<T, N> d;
public:
template <typename ... Ts>
Vec (Ts ... ts) : d{{ ts... }}
{ }
T & operator[] (int i)
{ return d[i]; }
T const & operator[] (int i) const
{ return d[i]; }
};
template <std::size_t I, typename F, typename ... Args>
auto applyH2 (F && f, Args ... as)
{ return f(extrV<I>(as)...); }
template <std::size_t ... Is, typename F, typename ... Args>
auto applyH1 (std::index_sequence<Is...> const &, F && f, Args ... as)
-> Vec<decltype(applyH2<0U>(f, as...)), sizeof...(Is)>
{ return { applyH2<Is>(f, as...)... }; }
template <typename F, typename ... Args, std::size_t N = dimVecV<Args...>>
auto apply (F && f, Args ... as)
{ return applyH1(std::make_index_sequence<N>{}, f, as...); }
long foo (int a, int b)
{ return a + b + 42; }
int main ()
{
Vec<int, 3U> v3;
Vec<int, 2U> v2;
auto r1 { apply(foo, v2, v2) };
auto r2 { apply(foo, v3, v3) };
auto r3 { apply(foo, v3, 0) };
static_assert( std::is_same<decltype(r1), Vec<long, 2U>>{}, "!" );
static_assert( std::is_same<decltype(r2), Vec<long, 3U>>{}, "!" );
static_assert( std::is_same<decltype(r3), Vec<long, 3U>>{}, "!" );
// apply(foo, v2, v3); // compilation error
// apply(foo, 1, 2); // compilation error
}
You can achieve what you want through a combination of partial template specialization and parameter pack extension.
#include <array>
template<typename T, std::size_t N>
using Vec = std::array<T, N>;
template<typename T>
struct extract {
static auto exec(T const& v, std::size_t) {return v;}
enum { size = 1 };
};
template<typename T, std::size_t N>
struct extract<Vec<T,N>> {
static auto exec(Vec<T,N> const& v, std::size_t i) {return v[i];}
enum {size = N};
};
template<typename T>
auto extract_v(T const& v, std::size_t i) {return extract<T>::exec(v, i);}
template<typename... args>
struct extract_size {
enum {size = 1};
};
template<typename first, typename... rest>
struct extract_size<first, rest...> {
enum {
rest_size_ = extract_size<rest...>::size,
self_size_ = extract<first>::size,
size = rest_size_ > self_size_ ? rest_size_ : self_size_
};
static_assert(self_size_ == 1 || rest_size_ == 1 || rest_size_ == self_size_, "");
};
template<typename F, typename... args_t>
auto apply(F const& cb, args_t&&... args) {
constexpr std::size_t size = extract_size<std::decay_t<args_t>...>::size;
using result_t = decltype(cb(extract_v(std::forward<args_t>(args),0)...));
Vec<result_t, size> result;
for(std::size_t i = 0 ; i < size ; ++i) {
result[i] = cb(extract_v(std::forward<args_t>(args),i)...);
}
return result;
}

Append to std::array

Since I was not able to find such a function (incorrectly?), I'm trying to make a compile-time function (constexpr) function which takes a std::array<T,n> arr and a T t and returns a new std::array<T,n+1> with t added to the end of arr. I've started with something like this:
template <typename T, int n>
constexpr std::array<T,n+1> append(std::array<T,n> a, T t);
template <typename T>
constexpr std::array<T,1> append(std::array<T,0> a, T t)
{
return std::array<T,1>{t};
}
template <typename T>
constexpr std::array<T,2> append(std::array<T,1> a, T t)
{
return std::array<T,2>{a[0], t};
}
Here I get stuck. What I need is a way to expand a in the first n places of the initializer list, and then add t add the end. Is that possible? Or is there another way of doing this?
Of course, it is possible: std::index_sequence<I...> is your friend! You'd simply dispatch to a function which takes a suitable std::index_sequence<I...> as argument and expands the pack with all the values. For example:
template <typename T, std::size_t N, std::size_t... I>
constexpr std::array<T, N + 1>
append_aux(std::array<T, N> a, T t, std::index_sequence<I...>) {
return std::array<T, N + 1>{ a[I]..., t };
}
template <typename T, std::size_t N>
constexpr std::array<T, N + 1> append(std::array<T, N> a, T t) {
return append_aux(a, t, std::make_index_sequence<N>());
}
It was easy to extend Dietmar's answer to a util that lets you constexpr- catenate two arrays:
// constexpr util to catenate two array's.
//
// Usage:
//
// constexpr std::array<int, 2> a1 = { 1, 2 };
// constexpr std::array<int, 2> a2 = { 3, 4 };
//
// constexpr auto a3 = catenate_array(a1, a2);
template <typename T, std::size_t N, std::size_t M, std::size_t... I, std::size_t... J>
constexpr std::array<T, N + M>
catenate_array_aux(std::array<T, N> a1, std::array<T, M> a2, std::index_sequence<I...>, std::index_sequence<J...>) {
return std::array<T, N + M>{ a1[I]..., a2[J]... };
}
template <typename T, std::size_t N, std::size_t M>
constexpr std::array<T, N + M> catenate_array(std::array<T, N> a1, std::array<T, M> a2) {
return catenate_array_aux(a1, a2, std::make_index_sequence<N>(), std::make_index_sequence<M>());
}

constexpr, arrays and initialization

Is there anything in the world of C++ that would make what I'm trying to do possible?
template < typename T
, size_t Size >
struct array
{
constexpr T buf[Size];
constexpr size_t size() const { return Size; }
};
template < typename T
, size_t Size >
constexpr array<T,Size+1> push_back(array<T,Size> const& arr, T const& val)
{
array<T,Size+1> arr_out = {{arr.buf, val}};
return arr_out;
}
What I'm trying to do is create a new array initialized with the data in the other, and put a new element on the end.
Minus the constexpr I can get it to work by loop initializing in the push_back function. It appears you can't do that in constexpr functions, which makes some sense though I think a smart enough compiler could figure that out.
I'm pretty sure it can't be done, but I'd love to be shown wrong.
Indices trick, yay~
template < typename T
, size_t Size >
struct array
{
T buf[Size]; // non-static data members can't be constexpr
constexpr size_t size() const { return Size; }
};
namespace detail{
template< typename T, size_t N, size_t... Is>
constexpr array<T, N+1> push_back(array<T, N> const& arr, T const& val, indices<Is...>)
{
// can only do single return statement in constexpr
return {{arr.buf[Is]..., val}};
}
} // detail::
template < typename T, size_t Size >
constexpr array<T,Size+1> push_back(array<T,Size> const& arr, T const& val)
{
return detail::push_back(arr, val, build_indices<Size>{});
}
Live example.
Expanding on Xeo's answer, here is a version which forwards its arguments:
#include <boost/mpl/if.hpp>
#include <cstddef>
#include <utility>
#include <iostream>
template<typename T, std::size_t Size>
struct array
{
typedef T value_type;
T buf[Size];
constexpr std::size_t size() const { return Size; }
};
template<typename T>
struct array_size;
template<typename T, std::size_t Size>
struct array_size<array<T, Size>> {
static constexpr std::size_t value = Size;
};
template <typename T>
using Bare =
typename std::remove_cv<typename std::remove_reference<T>::type>::type;
template <typename T>
constexpr T&& forward(typename std::remove_reference<T>::type& t) noexcept {
return static_cast<T&&>(t);
}
template<typename Array>
using CVValueType = typename boost::mpl::if_<
std::is_const<Array>,
typename boost::mpl::if_<
std::is_volatile<Array>,
typename Array::value_type const volatile,
typename Array::value_type const>::type,
typename boost::mpl::if_<
std::is_volatile<Array>,
typename Array::value_type volatile,
typename Array::value_type>::type
>::type;
template<typename Array>
using ForwardType =
typename boost::mpl::if_c<
std::is_lvalue_reference<Array>::value,
CVValueType<typename std::remove_reference<Array>::type>&,
CVValueType<typename std::remove_reference<Array>::type>&&>::type;
template <typename Array>
constexpr ForwardType<Array> forward_element(
CVValueType<typename std::remove_reference<Array>::type>& t) noexcept
{
return static_cast<ForwardType<Array>>(t);
}
template <std::size_t... Is>
struct indices {};
template <std::size_t N, std::size_t... Is>
struct build_indices
: build_indices<N-1, N-1, Is...> {};
template <std::size_t... Is>
struct build_indices<0, Is...> : indices<Is...> {};
template<typename Array>
using Enlarged =
array<typename Bare<Array>::value_type, array_size<Bare<Array>>::value+1>;
template<typename Array, typename T, std::size_t... Is>
constexpr Enlarged<Array> push_back(Array&& arr, T&& val, indices<Is...>)
{
return {{forward_element<Array>(arr.buf[Is])..., forward<T>(val)}};
}
template <typename Array, typename T>
constexpr Enlarged<Array> push_back(Array&& arr, T&& val)
{
return push_back(
forward<Array>(arr),
forward<T>(val),
build_indices<array_size<Bare<Array>>::value>{});
}
namespace detail_
{
template < typename T
, size_t End >
struct push_backer
{
template < typename Array
, typename ... Args>
static constexpr auto push_back(Array const& arr, Args const& ... args) -> decltype(push_backer<T,End-1>::push_back(arr, arr.buf[End-1],args...))
{
return push_backer<T,End-1>::push_back(arr, arr.buf[End-1], args...);
}
};
template < typename T >
struct push_backer<T,0>
{
template < size_t Size
, typename ... Args>
static constexpr array<T,Size+1> push_back(array<T,Size> const& arr, Args const& ... args)
{
return array<T,Size+1>{{args...}};
}
};
}
template < typename T
, size_t Size >
constexpr array<T,Size+1> push_back(array<T,Size> const& arr, T const& val)
{
return detail_::push_backer<T,Size>::push_back(arr, val);
}