c++11 constexpr flatten list of std::array into array - c++

I am beginning with c++11, constexpr and template metaprogramming seems a nice way to save scarce ram on tiny microcontroler.
Is there a way to write a template to flatten a list of constexpr array, what
I need is a way to do :
constexpr std::array<int, 3> a1 = {1,2,3};
constexpr std::array<int, 2> a2 = {4,5};
constexpr auto a3 = make_flattened_array (a1,a2);
I use gcc 4.8.4 (arm-none-eabi), and can compile with std=c++11 or c++1y option if it is needed.

Notice - I understood your question as follows: you want to join those two arrays and flatten the result into a single, new array containing the concatenation of their elements.
You can accomplish your goal with three C++11+ concepts:
Variadic templates
constexpr expressions
Parameter pack
You start by creating a template (an empty shell) to start designing your recursive-fashion list flattening function:
template<unsigned N1, unsigned N2>
constexpr std::array<int, N1+N2> concat(const std::array<int, N1>& a1, const std::array<int, N2>& a2){
// TODO
}
so far so good: the constexpr specifier will hint the compiler to compile-time evaluate that function each time it can.
Now for the interesting part: std::array has (since c++1y) a constexpr overload for the operator[], this means you can write something like
template<unsigned N1, unsigned N2>
constexpr std::array<int, N1+N2> concat(const std::array<int, N1>& a1, const std::array<int, N2>& a2){
return std::array<int,N1+N2>{a1[0],a1[1],a1[2],a2[0],a2[1]};
}
(notice the aggregate-initialization to initialize the object from a series of integer values)
Obviously manually hard-coding all the index accesses to the values of the two arrays is no better than just declaring the concatenated array itself. The concept that will save the day is the following: Parameter Packs. A template parameter pack is a template parameter that accepts 0 or more template arguments. A template with at least one parameter pack is called variadic template.
The cool thing is the ability of expanding the parameter pack into specified locations like:
#include <iostream>
#include <array>
template<unsigned... Num>
std::array<int, 5> function(const std::array<int,5>& source) {
return std::array<int,5>{source[Num]...};
}
int main() {
std::array<int,5> source{7,8,9,10,11};
std::array<int,5> res = function<0,1,2,3,4>(source);
for(int i=0; i<res.size(); ++i)
std::cout << res[i] << " "; // 7 8 9 10 11
return 0;
}
So the only thing we need right now is to be able to compile-time generate the "index series" like
std::array<int,5> res = function<0,1,2,3,4>(source);
^ ^ ^ ^ ^
At this point we can again take advantage of the parameter packs in conjunction with an inheritance mechanism: the idea is to have a deeply nested hierarchy of derived : base : other_base : another_base : ... classes which would "accumulate" the indices into the parameter pack and terminate the "recursion" when the index reaches 0. If you didn't understand the previous sentence don't worry and take a look at the following example:
std::array<int, 3> a1{42,26,77};
// goal: having "Is" = {0,1,2} i.e. a1's valid indices
template<unsigned... Is> struct seq;
we can generate a sequence of indices in the following way:
template<unsigned N, unsigned... Is>
struct gen_seq : gen_seq<N-1, Is...>{}; // each time decrement the index and go on
template<unsigned... Is>
struct gen_seq<0 /*stops the recursion*/, Is...> : /* generate the sequence */seq<Is...>{};
std::array<int, 3> a1{42,26,77};
gen_seq<3>{};
There's something missing anyway: the code above will start with gen_seq<3, (nothing)> and instantiate the specified template which will instantiate the gen_seq<2, (nothing)> as its base class that will instantiate the gen_seq<1, (nothing)> as its base class that will instantiate the gen_seq<0, (nothing)> as its base class that will instantiate the seq<(nothing)> as final sequence.
The sequence is '(nothing)', something is wrong..
In order to "accumulate" the indices into the parameter pack you need to "add a copy" of the decreased index to the parameter pack at each recursion:
template<unsigned N, unsigned... Is>
struct gen_seq : gen_seq<N-1, /*This copy goes into the parameter pack*/ N-1, Is...>{};
template<unsigned... Is>
struct gen_seq<0 /*Stops the recursion*/, Is...> : /*Generate the sequence*/seq<Is...>{};
template<unsigned... Is> struct seq{};
// Using '/' to denote (nothing)
gen_seq<3,/> : gen_seq<2, 2,/> : gen_seq<1, 1,2,/> : gen_seq<0, 0,1,2,/> : seq<0,1,2,/> .
so now we're able to recollect all the pieces together and generate two sequences of indices: one for the first array and one for the second array and concatenate them together into a new return array which will hold the concatenated and flattened union of the two arrays (like appending them together).
The following code, at this point, should be easily comprehensible:
#include <iostream>
#include <array>
template<unsigned... Is> struct seq{};
template<unsigned N, unsigned... Is>
struct gen_seq : gen_seq<N-1, N-1, Is...>{};
template<unsigned... Is>
struct gen_seq<0, Is...> : seq<Is...>{};
template<unsigned N1, unsigned... I1, unsigned N2, unsigned... I2>
// Expansion pack
constexpr std::array<int, N1+N2> concat(const std::array<int, N1>& a1, const std::array<int, N2>& a2, seq<I1...>, seq<I2...>){
return { a1[I1]..., a2[I2]... };
}
template<unsigned N1, unsigned N2>
// Initializer for the recursion
constexpr std::array<int, N1+N2> concat(const std::array<int, N1>& a1, const std::array<int, N2>& a2){
return concat(a1, a2, gen_seq<N1>{}, gen_seq<N2>{});
}
int main() {
constexpr std::array<int, 3> a1 = {1,2,3};
constexpr std::array<int, 2> a2 = {4,5};
constexpr std::array<int,5> res = concat(a1,a2);
for(int i=0; i<res.size(); ++i)
std::cout << res[i] << " "; // 1 2 3 4 5
return 0;
}
http://ideone.com/HeLLDm
References:
https://stackoverflow.com/a/13294458/1938163
http://en.cppreference.com/
http://en.wikipedia.org

With C++1y, an implementation may (although it is not required) allow std::tuple_cat to work with any tuple-like types, and not just std::tuple<T...>. In our case, std::array<T, N> is such a type. So we could attempt:
constexpr std::array<int, 3> a1 = {1, 2, 3};
constexpr std::array<int, 2> a2 = {4, 5};
constexpr auto a3 = std::tuple_cat(a1, a2);
// note:
// not possible
// constexpr auto e = a3[3]
// instead
constexpr auto e = std::get<3>(a3);
As it so happens though, the result of a call to std::tuple_cat is a tuple, not an array. It is then possible to turn an std::tuple<T, T,… , T> into an std::array<T, N>:
template<
typename Tuple,
typename VTuple = std::remove_reference_t<Tuple>,
std::size_t... Indices
>
constexpr std::array<
std::common_type_t<std::tuple_element_t<Indices, VTuple>...>,
sizeof...(Indices)
>
to_array(Tuple&& tuple, std::index_sequence<Indices...>)
{
return { std::get<Indices>(std::forward<Tuple>(tuple))... };
}
template<typename Tuple, typename VTuple = std::remove_reference_t<Tuple>>
constexpr decltype(auto) to_array(Tuple&& tuple)
{
return to_array(
std::forward<Tuple>(tuple),
std::make_index_sequence<std::tuple_size<VTuple>::value> {} );
}
(As it turns out this to_array implementation converts any tuple-like into an array as long as the tuple element types are compatible.)
Here’s a live example for GCC 4.8, filling in some of the C++1y features not yet supported.

Luc's post answer the question.
But for fun, here is a C++14 solution without template metaprogramming, just pure constexpr.
There is a catch though, generalized constexpr was voted into the standard core language more than one year ago, but the STL still isn't updated yet...
As an experiment, open the header <array> and add an obviously missing constexpr for non-const operator[]
constexpr reference operator[](size_type n);
Also open <numeric> and turn std::accumulate into a constexpr function
template <class InputIterator, class T>
constexpr T accumulate(InputIterator first, InputIterator last, T init);
Now we can do :
#include <iostream>
#include <array>
#include <numeric>
template <typename T, size_t... sz>
constexpr auto make_flattened_array(std::array<T, sz>... ar)
{
constexpr size_t NB_ARRAY = sizeof...(ar);
T* datas[NB_ARRAY] = {&ar[0]...};
constexpr size_t lengths[NB_ARRAY] = {ar.size()...};
constexpr size_t FLATLENGTH = std::accumulate(lengths, lengths + NB_ARRAY, 0);
std::array<T, FLATLENGTH> flat_a = {0};
int index = 0;
for(int i = 0; i < NB_ARRAY; i++)
{
for(int j = 0; j < lengths[i]; j++)
{
flat_a[index] = datas[i][j];
index++;
}
}
return flat_a;
}
int main()
{
constexpr std::array<int, 3> a1 = {1,2,3};
constexpr std::array<int, 2> a2 = {4,5};
constexpr std::array<int, 4> a3 = {6,7,8,9};
constexpr auto a = make_flattened_array(a1, a2, a3);
for(int i = 0; i < a.size(); i++)
std::cout << a[i] << std::endl;
}
(Compile and run on clang trunk)

Another way is using expression templates. It does not copy arrays.
A sketch:
#include <array>
template<class L, class R>
struct AddOp
{
L const& l_;
R const& r_;
typedef typename L::value_type value_type;
AddOp operator=(AddOp const&) = delete;
constexpr value_type const& operator[](size_t idx) const {
return idx < l_.size() ? l_[idx] : r_[idx - l_.size()];
}
constexpr std::size_t size() const {
return l_.size() + r_.size();
}
// Implement the rest of std::array<> interface as needed.
};
template<class L, class R>
constexpr AddOp<L, R> make_flattened_array(L const& l, R const& r) {
return {l, r};
}
constexpr std::array<int, 3> a1 = {1,2,3};
constexpr std::array<int, 2> a2 = {4,5};
constexpr std::array<int, 2> a3 = {6};
constexpr auto a4 = make_flattened_array(a1,a2);
constexpr auto a5 = make_flattened_array(a4,a3);
int main() {
constexpr auto x = a5[1];
constexpr auto y = a5[4];
constexpr auto z = a5[5];
}

Related

intializing a std::array of objects that don't have default constructor from another constexpr std::array

Is it possible to initialize an array of objects whose members are initialized from another constexpr array of trivial objects. For example I have the following
struct X
{
X(int y): _x(y){}
int _x;
};
struct Z
{
static constexpr std::array<int, 4> arr = {1,6,0,4};
// How does one implement make_array below that constructs
// X[0] from arr[0], X[1] from arr[1], etc.
// Is it even feasible in C++14/17?
std::array<X, arr.size()> xArr = make_array( );
};
With std::index_sequence:
template <typename T, typename U, std::size_t N, std::size_t ... Is>
constexpr std::array<T, N> make_array(const std::array<U, N>& a, std::index_sequence<Is...>)
{
return {{T(a[Is])...}};
}
template <typename T, typename U, std::size_t N>
constexpr std::array<T, N> make_array(const std::array<U, N>& a)
{
return make_array<T>(a, std::make_index_sequence<N>());
}
Usage:
static constexpr std::array<int, 4> arr = {1,6,0,4};
/*constexpr*/ std::array<X, arr.size()> xArr = make_array<X>(arr);
Demo
One solution is to use a parameter pack and expand it to construct an initializer list for the array.
I use a std::index_sequence and std::make_index_sequence to construct a parameter pack which contains the index of the elements (simply 0, 1, 2, ..., N-1) then I unpack those indexes into an initializer list :
#include <array>
#include <utility>
// O : output type; type to convert elements to
// T : input type
// N : number of elements
// I : parameter pack of indexes
template<class O, class T, std::size_t N, std::size_t ... I>
auto make_array_impl(const std::array<T, N> & p_input, std::index_sequence<I...>) -> std::array<O, N>
{
// Unpack the parameter pack into an initializer list
// Constructs an `O` from each element in order
return {O{p_input[I]}...};
}
// O : output type; type to convert elements to
// T : input type
// N : number of elements
template<class O, class T, std::size_t N>
auto make_array(const std::array<T, N> & p_input)
{
// Helper function to automatically generate the parameter pack
return make_array_impl<O>(p_input, std::make_index_sequence<N>{});
}
Example : https://godbolt.org/z/dhEGaG

C++: Can I have non-static member variable templates?

I'm trying to write a bit of code that requires me to have a lot of std::arrays in a container class. These arrays are all of varying sizes (all consecutive from 2-16, if that matters), and there is exactly one of each size. I want to put them in a container class, and be able to access them with templates.
It's probably easier to explain with code. I want something like this:
class ContainerClass {
public:
// I want to declare some number of arrays right here, all of different
// sizes, ranging from 2-16. I'd like to be able to access them as
// arr<2> through arr<16>.
// This code gives a compiler error, understandably.
// But this is what I'd think it'd look like.
template <size_t N> // I also need to find a way to restrict N to 2 through 16.
std::array<int, N> arr;
// An example method of how I want to be able to use this.
template <size_t N>
void printOutArr() {
for (int i = 0; i < N; i++) {
std::cout << arr<N>[i] << std::endl;
}
}
};
I'd like the code to expand out as if it just had 15 arrays in it, from 2-16. Like this, but with templates:
class ContainerClass {
public:
std::array<int, 2> arr2;
std::array<int, 3> arr3;
std::array<int, 4> arr4;
std::array<int, 5> arr5;
// ... and so on.
};
From what I understand, C++ supports variable templates, but it seems like it's only for static members in classes. Is there an alternative that could behave similarly (preferably with as little overhead as possible)?
If you need more information, please ask.
Thanks in advance.
Can I have non-static member variable templates?
No.
However, you can use templates to generate a list of members like you describe. Here is an example using recursive inheritance:
template<class T, std::size_t base, std::size_t size>
class Stair;
template<class T, std::size_t base>
class Stair<T, base, base> {};
template<class T, std::size_t base, std::size_t size>
class Stair : Stair<T, base, size - 1> {
protected:
std::array<T, size> arr;
public:
template<std::size_t s>
std::array<T, s>& array() {
return Stair<T, base, s>::arr;
}
};
int main()
{
Stair<int, 2, 10> s;
auto& arr = s.array<9>();
I think I may have a solution using recursive templates and std::tuple. I compiled and tested it using gcc 7.3.0.
This makes me feel dirty, but it seems to work.
#include <iostream>
#include <array>
#include <tuple>
#include <type_traits>
// Forward declare A since there is a circular dependency between A and Arrays
template <size_t size, size_t start, typename... T>
struct A;
// If size is greater than start define a type that is an A::ArrayTuple from the
// next step down (size - 1) otherwise type is void
template <size_t size, size_t start, typename E, typename... T>
struct Arrays {
using type = typename std::conditional<(size > start),
typename A<size-1, start, E, T...>::ArrayTuple,
void
>::type;
};
// Use template recursion to begin at size and define std::array<int, size>
// to add to a tuple and continue marching backwards (size--) until size == start
// When size == start take all of the std::arrays and put them into a std::tuple
//
// A<size, start>::ArrayTuple will be a tuple of length (size - start + 1) where
// the first element is std::array<int, start>, the second element is
// std::array<int, start + 1> continuing until std::array<int, size>
template <size_t size, size_t start, typename... T>
struct A {
using Array = typename std::array<int, size>;
using ArrayTuple = typename std::conditional<(size == start),
typename std::tuple<Array, T...>,
typename Arrays<size, start, Array, T...>::type
>::type;
};
// This specialization is necessary to avoid infinite template recursion
template <size_t start, typename... T>
struct A<0, start, T...> {
using Array = void;
using ArrayTuple = void;
};
template <size_t size, size_t start = 1>
class Container {
public:
using ArrayTuple = typename A<size, start>::ArrayTuple;
// Shorthand way to the get type of the Nth element in ArrayTuple
template <size_t N>
using TupleElementType = typename
std::tuple_element<N-start, ArrayTuple>::type;
ArrayTuple arrays_;
// Returns a reference to the tuple element that has the type of std::array<int, N>
template <size_t N>
TupleElementType<N>& get_array() {
// Static assertion that the size of the array at the Nth element is equal to N
static_assert(std::tuple_size< TupleElementType<N> >::value == N);
return std::get<N-start>(arrays_);
}
// Prints all elements of the tuple element that has the type of std::array<int, N>
template <size_t N>
void print_array() {
auto& arr = get_array<N>();
std::cout << "Array Size: " << arr.size() << std::endl;
for (int i = 0; i < arr.size(); i++) {
std::cout << arr[i] << std::endl;
}
}
};
int main() {
// Create a new Container object where the arrays_ member will be
// a tuple with 15 elements:
// std::tuple< std::array<int, 2>, std::array<int, 3> ... std::array<int, 16> >
Container<16,2> ctr;
auto& arr2 = ctr.get_array<2>();
arr2[0] = 20;
arr2[1] = 21;
//ctr.print_array<1>(); // Compiler error since 1 < the ctr start (2)
ctr.print_array<2>(); // prints 20 and 21
ctr.print_array<3>(); // prints 3 zeros
ctr.print_array<16>(); // prints 16 zeros
//ctr.print_array<17>(); // Compiler error since 17 > the ctr size (16)
//int x(ctr.arrays_); // Compiler error - uncomment to see the type of ctr.arrays_
return 0;
}
Here's the output from the compiler if I uncomment that line where I try to declare int x showing the type of ctr.arrays_:
so.cpp: In function ‘int main()’:
so.cpp:90:22: error: cannot convert ‘Container<16, 2>::ArrayTuple {aka std::tuple<std::array<int, 2>, std::array<int, 3>, std::array<int, 4>, std::array<int, 5>, std::array<int, 6>, std::array<int, 7>, std::array<int, 8>, std::array<int, 9>, std::array<int, 10>, std::array<int, 11>, std::array<int, 12>, std::array<int, 13>, std::array<int, 14>, std::array<int, 15>, std::array<int, 16> >}’ to ‘int’ in initialization
int x(ctr.arrays_); // Compiler error - uncomment to see the type of ctr.arrays_

Combine two or more arrays of different size to one array at compiletime

I was not able to find an answer on how to combine two or more arrays at compiletime in modern c++.
#include <array>
#include <cstdint>
const std::array<std::uint8_t, 1> one_elem = {1};
const std::array<std::uint8_t, 2> two_elem = {2, 3};
const std::array<std::uint8_t, 3> all = {one_elem, two_elem};
// expected: all == {1, 2, 3}
I would be glad with anything that is somewhat easy to read, e.g.
std::uint8_t one_elem[] = {1};
std::uint8_t two_elem[] = {2, 3};
std::uint8_t all[] = {one_elem, two_elem}; // cannot be that hard
Is there a way? What can I do to get this solved?
If you are using C++17, you can do this:
template <typename T, std::size_t N1, std::size_t N2>
constexpr std::array<T, N1 + N2> concat(std::array<T, N1> lhs, std::array<T, N2> rhs)
{
std::array<T, N1 + N2> result{};
std::size_t index = 0;
for (auto& el : lhs) {
result[index] = std::move(el);
++index;
}
for (auto& el : rhs) {
result[index] = std::move(el);
++index;
}
return result;
}
constexpr std::array<std::uint8_t, 1> one_elem = {1};
constexpr std::array<std::uint8_t, 2> two_elem = {2, 3};
constexpr std::array<std::uint8_t, 3> all = concat(one_elem, two_elem);
It doesn't work in C++14, because std::array isn't constexpr friendly until C++17. However, if you don't care that the final result is constexpr, you can simply mark each variable as const, and this will work:
const std::array<std::uint8_t, 1> one_elem = {1};
const std::array<std::uint8_t, 2> two_elem = {2, 3};
const std::array<std::uint8_t, 3> all = concat(one_elem, two_elem);
The compiler will almost certainly optimize the concat away.
If you need a C++14 solution, we have to create it via std::array's constructor, so it's not nearly as nice:
#include <array>
#include <cstdint>
#include <cstddef>
#include <type_traits>
// We need to have two parameter packs in order to
// unpack both arrays. The easiest way I could think of for
// doing so is by using a parameter pack on a template class
template <std::size_t... I1s>
struct ConcatHelper
{
template <typename T, std::size_t... I2s>
static constexpr std::array<T, sizeof...(I1s) + sizeof...(I2s)>
concat(std::array<T, sizeof...(I1s)> const& lhs,
std::array<T, sizeof...(I2s)> const& rhs,
std::index_sequence<I2s...>)
{
return { lhs[I1s]... , rhs[I2s]... };
}
};
// Makes it easier to get the correct ConcatHelper if we know a
// std::index_sequence. There is no implementation for this function,
// since we are only getting its type via decltype()
template <std::size_t... I1s>
ConcatHelper<I1s...> get_helper_type(std::index_sequence<I1s...>);
template <typename T, std::size_t N1, std::size_t N2>
constexpr std::array<T, N1 + N2> concat(std::array<T, N1> const& lhs, std::array<T, N2> const& rhs)
{
return decltype(get_helper_type(std::make_index_sequence<N1>{}))::concat(lhs, rhs, std::make_index_sequence<N2>{});
}
constexpr std::array<std::uint8_t, 1> one_elem = {1};
constexpr std::array<std::uint8_t, 2> two_elem = {2, 3};
constexpr std::array<std::uint8_t, 3> all = concat(one_elem, two_elem);
There's already a way to concatenate arrays in C++: std::tuple_cat. The only problem is that it gives you a tuple<uint8_t, uint8_t, uint8_t> instead of a std::array<uint8_t, 3>. But that problem is solvable with a different standard library function: std::apply. That one is technically C++17, but is implementable in C++14. You just need a funject:
struct to_array_t {
template <class T, class... Ts>
std::array<std::decay_t<T>, sizeof...(Ts)+1> operator()(T&& t, Ts&&... ts) const {
return {{std::forward<T>(t), std::forward<Ts>(ts)...}};
}
} to_array{};
and then you can use it:
auto all = std::apply(to_array, std::tuple_cat(one_elem, two_elem));
Which might be easier to just hide behind a function:
template <class Target=void, class... TupleLike>
auto array_concat(TupleLike&&... tuples) {
return std::apply([](auto&& first, auto&&... rest){
using T = std::conditional_t<
!std::is_void<Target>::value, Target, std::decay_t<decltype(first)>>;
return std::array<T, sizeof...(rest)+1>{{
decltype(first)(first), decltype(rest)(rest)...
}};
}, std::tuple_cat(std::forward<TupleLike>(tuples)...));
}
Perfect forwarding with lambdas is a bit ugly. The Target type is allow the user to specify a type for the resulting array - otherwise it will be selected as the decayed type of the first element.

Implementing std::array-like constructors in other classes

In all the modern C++ compilers I've worked with, the following is legal:
std::array<float, 4> a = {1, 2, 3, 4};
I'm trying to make my own class that has similar construction semantics, but I'm running into an annoying problem. Consider the following attempt:
#include <array>
#include <cstddef>
template<std::size_t n>
class float_vec
{
private:
std::array<float, n> underlying_array;
public:
template<typename... Types>
float_vec(Types... args)
: underlying_array{{args...}}
{
}
};
int main()
{
float_vec<4> v = {1, 2, 3, 4}; // error here
}
When using int literals like above, the compiler complains it can't implicitly convert int to float. I think it works in the std::array example, though, because the values given are compile-time constants known to be within the domain of float. Here, on the other hand, the variadic template uses int for the parameter types and the conversion happens within the constructor's initializer list where the values aren't known at compile-time.
I don't want to do an explicit cast in the constructor since that would then allow for all numeric values even if they can't be represented by float.
The only way I can think of to get what I want is to somehow have a variable number of parameters, but of a specific type (in this case, I'd want float). I'm aware of std::initializer_list, but I'd like to be able to enforce the number of parameters at compile time as well.
Any ideas? Is what I want even possible with C++11? Anything new proposed for C++14 that will solve this?
A little trick is to use constructor inheritance. Just make your class derive from another class which has a pack of the parameters you want.
template <class T, std::size_t N, class Seq = repeat_types<N, T>>
struct _array_impl;
template <class T, std::size_t N, class... Seq>
struct _array_impl<T, N, type_sequence<Seq...>>
{
_array_impl(Seq... elements) : _data{elements...} {}
const T& operator[](std::size_t i) const { return _data[i]; }
T _data[N];
};
template <class T, std::size_t N>
struct array : _array_impl<T, N>
{
using _array_impl<T, N>::_array_impl;
};
int main() {
array<float, 4> a {1, 2, 3, 4};
for (int i = 0; i < 4; i++)
std::cout << a[i] << std::endl;
return 0;
}
Here is a sample implementation of the repeat_types utility. This sample uses logarithmic template recursion, which is a little less intuitive to implement than with linear recursion.
template <class... T>
struct type_sequence
{
static constexpr inline std::size_t size() noexcept { return sizeof...(T); }
};
template <class, class>
struct _concatenate_sequences_impl;
template <class... T, class... U>
struct _concatenate_sequences_impl<type_sequence<T...>, type_sequence<U...>>
{ using type = type_sequence<T..., U...>; };
template <class T, class U>
using concatenate_sequences = typename _concatenate_sequences_impl<T, U>::type;
template <std::size_t N, class T>
struct _repeat_sequence_impl
{ using type = concatenate_sequences<
typename _repeat_sequence_impl<N/2, T>::type,
typename _repeat_sequence_impl<N - N/2, T>::type>; };
template <class T>
struct _repeat_sequence_impl<1, T>
{ using type = T; };
template <class... T>
struct _repeat_sequence_impl<0, type_sequence<T...>>
{ using type = type_sequence<>; };
template <std::size_t N, class... T>
using repeat_types = typename _repeat_sequence_impl<N, type_sequence<T...>>::type;
First of what you are seeing is the default aggregate initialization. It has been around since the earliest K&R C. If your type is an aggregate, it supports aggregate initialization already. Also, your example will most likely compile, but the correct way to initialize it is std::array<int, 3> x ={{1, 2, 3}}; (note the double braces).
What has been added in C++11 is the initializer_list construct which requires a bit of compiler magic to be implemented.
So, what you can do now is add constructors and assignment operators that accept a value of std::initializer_list and this will offer the same syntax for your type.
Example:
#include <initializer_list>
struct X {
X(std::initializer_list<int>) {
// do stuff
}
};
int main()
{
X x = {1, 2, 3};
return 0;
}
Why does your current approach not work? Because in C++11 std::initializer_list::size is not a constexpr or part of the initializer_list type. You cannot use it as a template parameter.
A few possible hacks: make your type an aggregate.
#include <array>
template<std::size_t N>
struct X {
std::array<int, N> x;
};
int main()
{
X<3> x = {{{1, 2, 3}}}; // triple braces required
return 0;
}
Provide a make_* function to deduce the number of arguments:
#include <array>
template<std::size_t N>
struct X {
std::array<int, N> x;
};
template<typename... T>
auto make_X(T... args) -> X<sizeof...(T)>
// might want to find the common_type of the argument pack as well
{ return X<sizeof...(T)>{{{args...}}}; }
int main()
{
auto x = make_X(1, 2, 3);
return 0;
}
If you use several braces to initialize the instance, you can leverage list-init of another type to accept these conversions for compile-time constants. Here's a version that uses a raw array, so you only need parens + braces for construction:
#include <array>
#include <cstddef>
template<int... Is> struct seq {};
template<int N, int... Is> struct gen_seq : gen_seq<N-1, N-1, Is...> {};
template<int... Is> struct gen_seq<0, Is...> : seq<Is...> {};
template<std::size_t n>
class float_vec
{
private:
std::array<float, n> underlying_array;
template<int... Is>
constexpr float_vec(float const(&arr)[n], seq<Is...>)
: underlying_array{{arr[Is]...}}
{}
public:
constexpr float_vec(float const(&arr)[n])
: float_vec(arr, gen_seq<n>{})
{}
};
int main()
{
float_vec<4> v0 ({1, 2, 3, 4}); // fine
float_vec<4> v1 {{1, 2, 3, 4}}; // fine
float_vec<4> v2 = {{1, 2, 3, 4}}; // fine
}
Explicitly specify that the data type for the initialization to floating point type. You can do this by doing "1.0f" instead of putting "1". If it is a double, put "1.0d". If it is a long, put "1l" and for unsigned long put "1ul" and so on..
I've tested it here: http://coliru.stacked-crooked.com/a/396f5d418cbd3f14
and here: http://ideone.com/ZLiMhg
Your code was fine. You just initialized the class a bit incorrect.
#include <array>
#include <cstddef>
#include <iostream>
template<std::size_t n>
class float_vec
{
private:
std::array<float, n> underlying_array;
public:
template<typename... Types>
float_vec(Types... args)
: underlying_array{{args...}}
{
}
float get(int index) {return underlying_array[index];}
};
int main()
{
float_vec<4> v = {1.5f, 2.0f, 3.0f, 4.5f}; //works fine now..
for (int i = 0; i < 4; ++i)
std::cout<<v.get(i)<<" ";
}

C++11: Number of Variadic Template Function Parameters?

How can I get a count of the number of arguments to a variadic template function?
ie:
template<typename... T>
void f(const T&... t)
{
int n = number_of_args(t);
...
}
What is the best way to implement number_of_args in the above?
Just write this:
const std::size_t n = sizeof...(T); //you may use `constexpr` instead of `const`
Note that n is a constant expression (i.e known at compile-time), which means you may use it where constant expression is needed, such as:
std::array<int, n> a; //array of n elements
std::array<int, 2*n> b; //array of (2*n) elements
auto middle = std::get<n/2>(tupleInstance);
Note that if you want to compute aggregated size of the packed types (as opposed to number of types in the pack), then you've to do something like this:
template<std::size_t ...>
struct add_all : std::integral_constant< std::size_t,0 > {};
template<std::size_t X, std::size_t ... Xs>
struct add_all<X,Xs...> :
std::integral_constant< std::size_t, X + add_all<Xs...>::value > {};
then do this:
constexpr auto size = add_all< sizeof(T)... >::value;
In C++17 (and later), computing the sum of size of the types is much simpler using fold expression:
constexpr auto size = (sizeof(T) + ...);
#include <iostream>
template<typename ...Args>
struct SomeStruct
{
static const int size = sizeof...(Args);
};
template<typename... T>
void f(const T&... t)
{
// this is first way to get the number of arguments
constexpr auto size = sizeof...(T);
std::cout<<size <<std::endl;
}
int main ()
{
f("Raje", 2, 4, "ASH");
// this is 2nd way to get the number of arguments
std::cout<<SomeStruct<int, std::string>::size<<std::endl;
return 0;
}