less verbose way to declare multidimensional std::array - c++

Short question:
Is there a shorter way to do this
array<array<atomic<int>,n>,m> matrix;
I was hoping for something like
array< atomic< int>,n,m> matrix;
but it doesnt work...

A template alias might help out:
#include <array>
template <class T, unsigned I, unsigned J>
using Matrix = std::array<std::array<T, J>, I>;
int main()
{
Matrix<int, 3, 4> matrix;
}

A palatable workaround for compilers that don't support template aliases yet is to use a simple metafunction to generate the type:
#include <cstddef>
#include <array>
template<class T, std::size_t RowsN, std::size_t ColumnsN>
struct Matrix
{
typedef std::array<std::array<T, ColumnsN>, RowsN> type; // row major
private:
Matrix(); // prevent accidental construction of the metafunction itself
};
int main()
{
Matrix<int, 3, 4>::type matrix;
}

Solution using variadic templates (slightly more complex than the template alias, but more general purpose)
template <typename T, std::size_t thisSize, std::size_t ... otherSizes>
class multi_array : private std::array<multi_array<T, otherSizes...>, thisSize>
{
using base_array = std::array<multi_array<T, otherSizes...>, thisSize>;
public:
using base_array::operator[];
// TODO: add more using statements to make methods
// visible. This is less typing (and less error-prone)
// than forwarding to the base_array type.
};
template <typename T, std::size_t thisSize>
class multi_array<T, thisSize> : private std::array<T, thisSize>
{
using base_array = std::array<T, thisSize>;
public:
using base_array::operator[];
// TODO: add more using statements to make methods
// visible. This is less typing (and less error-prone)
// than forwarding to the base_array type.
};
There might be some improvement on assigning to non-leaves of the array that could be made.
I tested with a relatively recent build of clang/LLVM.
Enjoy!

When nested, std::array can become very hard to read and unnecessarily verbose. The opposite ordering of the dimensions can be especially confusing.
For example:
std::array < std::array <int, 3 > , 5 > arr1;
compared to
char c_arr [5][3];
Also, note that begin(), end() and size() all return meaningless values when you nest std::array.
For these reasons I've created my own fixed size multidimensional array containers, array_2d and array_3d. They have the advantage that they work with C++98.
They are analogous to std::array but for multidimensional arrays of 2 and 3 dimensions. They are safer and have no worse performance than built-in multidimensional arrays. I didn't include a container for multidimensional arrays with dimensions greater than 3 as they are uncommon. In C++11 a variadic template version could be made which supports an arbitrary number of dimensions (Something like Michael Price's example).
An example of the two-dimensional variant:
//Create an array 3 x 5 (Notice the extra pair of braces)
fsma::array_2d <double, 3, 5> my2darr = {{
{ 32.19, 47.29, 31.99, 19.11, 11.19},
{ 11.29, 22.49, 33.47, 17.29, 5.01 },
{ 41.97, 22.09, 9.76, 22.55, 6.22 }
}};
Full documentation is available here:
http://fsma.googlecode.com/files/fsma.html
You can download the library here:
http://fsma.googlecode.com/files/fsma.zip

Here's a simple, generic version:
template <typename T, size_t d1, size_t d2, size_t... ds>
struct GetMultiDimArray
{
using type = std::array<typename GetMultiDimArray<T, d2, ds...>::type, d1>;
};
template <typename T, size_t d1, size_t d2>
struct GetMultiDimArray<T, d1, d2>
{
using type = std::array<std::array<T, d2>, d1>;
};
template <typename T, size_t d1, size_t d2, size_t... ds>
using MultiDimArray = typename GetMultiDimArray<T, d1, d2, ds...>::type;
// Usage:
MultiDimArray<int, 3, 2> arr {1, 2, 3, 4, 5, 6};
assert(arr[1][1] == 4);

Related

Adding new constructors to a specialised template class

I have a class defining an array of fixed length n, with some methods.
template<int n>
struct array_container{
/* some code here */
int array[n];
};
Let's say I want to add a constructor to array_container<3>, something along the lines of:
array_container<3>::array_container(int a0, int a1 ,int a2){
array[0] = a0;
array[1] = a1;
array[2] = a1;
}
I know of two ways to do this:
One is to copy the entire code of the generic class, replacing n with 3, and add my constructor:
template<>
struct array_container<3>{
/* some code here */
int array[3];
array_container(int a0, int a1 ,int a2){
array[0] = a0;
array[1] = a1;
array[2] = a1; }
};
This works correctly, but has the disadvantage of needing to copy all the code and methods from the generic base.
Another method is to add a constructor array_container(int a0, int a1, int a2); in the generic class, then define:
template<>
array_container<3>:: array_container(int a0, int a1 ,int a2){
array[0] = a0;
array[1] = a1;
array[2] = a1; }
This has the disadvantage of populating my generic base class with at best undefined or at worst incorrect constructors, such as
array_container<2>(int a0, int a1 ,int a2) (undefined or incorrect depending on whether or not I add the definition to the generic base or not).
Is there any approach that avoids both pitfalls? Ie. doesn't need to copy-paste the entire generic base code for the specialization, and doesn't add unnecessary constructors to the generic base?
Why not simply use
template <std::size_t size>
struct MyArrayWithFunctions : std::array<int, size> {
/* some functions */
};
std::array allows aggregate initialization and even deduces the size so you can simply write
MyArrayWithFunctions arr{1,2,3};
You can add a deduction guideline for your own class but why re-implement array?
Is there any approach that avoids both pitfalls? Ie. doesn't need to copy-paste the entire generic base code for the specialization, and doesn't add unnecessary constructors to the generic base?
Ignoring the fact that, as #Goswin von Brederlow mentions, you seem to be reinventing the wheel (std::array and aggregate initialization), C++20's requires-expressions allows you to define constructors in a primary template that are constrained to only certain specializations. E.g.:
#include <type_traits>
// Helper trait for constraining a ctor to a set
// of specializations.
template <int n, int... ns> struct is_one_of {
static constexpr bool value{false};
};
template <int n, int n0, int... ns> struct is_one_of<n, n0, ns...> {
static constexpr bool value{(n == n0) || is_one_of<n, ns...>::value};
};
template <int n, int... ns>
inline constexpr bool is_one_of_v{is_one_of<n, ns...>::value};
template <int n> struct array_container {
/* some code here */
int array[n];
// Constrained to n == 3 or n == 5.
template <typename... Args>
requires(std::is_same_v<Args, int> &&...) && (sizeof...(Args) == n) &&
is_one_of_v<n, 3, 5 /*, ... */> array_container(Args... args)
: array{args...} {}
};
// Demo.
array_container<3> arr3{1, 2, 3}; // OK
array_container<4> arr4{1, 2, 3, 4}; // Error (no matching ctor)
array_container<5> arr5{1, 2, 3, 4, 5}; // OK
The easier solution would be to construct it with an array(in place).
template<int n>
struct array_container{
int array[n];
array_container(std::array<int, n> arrayIn)
{
std::copy(arrayIn.begin(), arrayIn.end(), array);
}
};
Otherwise you can mess with variadic templates and parameter unpacking.
If happen to have a C++17 compiler you can use the following code:
#include <type_traits>
// class definition
template<int n>
struct array_container {
int array[n];
template<typename... Args, typename std::enable_if_t<(std::is_same_v<int, Args> && ...) && (sizeof...(Args) == n), bool> = true>
array_container(Args... args):
array{args...}
{}
};
// user defined template deduction guide
template<typename... Args>
array_container(Args...) -> array_container<sizeof...(Args)>;
and use it like
array_container<3> x {1,2,3};
or even let the size be deduced from the number of arguments like
// n deduced to 3 from the number of arguments
array_container x {1,2,3};
The constructor is a variadic template, taking any number of int arguments (the latter is enforced by the std::enable_if_t template parameter) and initializes the array member from them. A user defined deduction guide can be used to automatically deduce the n parameter from the number of arguments, that you pass to the constructor.
See it live on godbolt.

Is there any way to set all elements of a matrix to 1 in C++?

Say I am declaring a matrix like this--> long long int A[100][100]. Is there any way to assign the value 1 to all its elements at the time of declaring the matrix?
You can use the fill_n method and initialize array like this:
std::fill_n(&A[0][0], sizeof(A) / sizeof(**A), 1);
Check this for more information about fill_na.
Hope it solves your question.
Is there any way to assign the value 1 to all its elements at the time of declaring the matrix?
I interpret that as you want something constexpr and I'd use std::array instead of plain arrays since std::arrays are easier to work with.
I've made templates for make_array that creates a 1 dimensional array and templates for make_2d_array that creates a 2 dimensional array, using the first set of templates.
The goal of the templates is that if we for example want a 2d array with 2 rows and 3 columns of int with all ints initialized with 1, this should be created:
std::array<std::array<int, 3>, 2> A{{
{1, 1, 1},
{1, 1, 1}
}};
In the code below make_array<int, 3, 1>() will create the inner arrays, std::array<int, 3>{1, 1, 1}, and make_2d_array<int, 2, 3, 1>() will be equivalent to:
std::array<std::array<int, 3>, 2> A{
make_array<int, 3, 1>(), // std::array<int, 3>{1, 1, 1}
make_array<int, 3, 1>() // std::array<int, 3>{1, 1, 1}
};
These are a set of templates that can accomplish that:
#include <array>
#include <type_traits>
#include <utility>
//-------------------------------------------------------------
template <typename T, T init, std::size_t... X>
constexpr std::array<T, sizeof...(X)>
make_array(std::index_sequence<X...>) {
//
// used for folding only then discarded below
// |
return std::array<T, sizeof...(X)>{ ((void)(X), (init))... };
}
template <typename T, std::size_t X, T init = T{}>
constexpr std::array<T, X> make_array() {
return make_array<T, init>(std::make_index_sequence<X>{});
}
//-------------------------------------------------------------
template <typename T, T init, std::size_t X, std::size_t... Y>
constexpr std::array< std::array<T, X>, sizeof...(Y)>
make_2d_array(std::index_sequence<Y...>) {
return std::array< std::array<T, X>, sizeof...(Y)>{
//
// Y used for folding only and discarded
// |
((void)(Y), make_array<T, X, init>())...
};
}
template <typename T, std::size_t Y, std::size_t X, T init = T{}>
constexpr std::array<std::array<T, X>, Y>
make_2d_array() {
return make_2d_array<T, init, X>(std::make_index_sequence<Y>{});
}
It can then be used like this:
constexpr auto A = make_2d_array<int, 2, 3, 1>(); // Y=2, X=3, init value 1
Full demo
(click on the Cppinsights link top left in the demo if you'd like to see how the folding unfolds)
As shown in Ted Lyngmo's answer, instead of a long long int A[100][100] we can declare and initialize a std::array<std::array<long long, 100>, 100> using a constexpr function.
Since C++20, it's possible to declare a function with a consteval specifier which
specifies that a function is an immediate function, that is, every call to the function must produce a compile-time constant.
Moreover, the algorithm std::fill_n has been made constexpr.
So that we can write the following two function templates:
#include <algorithm>
#include <array>
template <class Type, std::size_t Size>
consteval auto make_1d_array(Type value)
{
std::array<Type, Size> result;
std::fill_n(result.begin(), Size, value);
return result;
}
template <class Type, std::size_t Rows, std::size_t Cols>
consteval auto make_2d_array(Type value)
{
std::array<std::array<Type, Cols>, Rows> result;
std::fill_n( result.begin(), Rows
, make_1d_array<Type, Cols>(value) );
return result;
}
Live demo.

Pass N-D array by reference to variadic function

I'd like to make the function multi_dimensional accept a multidimensional array by reference.
Can this be done with a variation of the syntax below which works for three_dimensional?
#include <utility>
// this works, but number of dimensions must be known (not variadic)
template <size_t x, size_t y, size_t z>
void three_dimensional(int (&nd_array)[x][y][z]) {}
// error: parameter packs not expanded with ‘...’
template <size_t... dims>
void multi_dimensional(int (&nd_array)[dims]...) {}
int main() {
int array[2][3][2] = {
{ {0,1}, {2,3}, {4,5} },
{ {6,7}, {8,9}, {10,11} }
};
three_dimensional(array); // OK
// multi_dimensional(array); // error: no matching function
return 0;
}
The main problem is that you cannot make the number of array dimensions itself variadic. So whichever way you go, you will almost certainly need a recursive approach of some sort to deal with the individual array layers. What exactly such approach should look like will mainly depend on what exactly you're planning to do with the array once it's been given to you.
If really all you want is a function that can be given any multi-dimensional array, then just write a function that can be given anything but only exists as long as that anything is an array:
template <typename T>
std::enable_if_t<std::is_array_v<T>> multi_dimensional(T& a)
{
constexpr int dimensions = std::rank_v<T>;
// ...
}
However, this by itself will most likely not get you very far. To actually do anything meaningful with the array you've been given, you will most likely need some recursive walking through subarrays. Unless you really just want to look at the topmost layer of the structure.
Another approach is to use a recursive template to peel back the individual array levels, for example:
// we've reached the bottom
template <typename T, int N>
void multi_dimensional(T (&a)[N])
{
// ...
}
// this matches any array with more than one dimension
template <typename T, int N, int M>
void multi_dimensional(T (&a)[N][M])
{
// peel off one dimension, invoke function for each element on next layer
for (int i = 0; i < N; ++i)
multi_dimensional(a[i]);
}
I would, however, suggest to at least consider using std::array<> instead of raw arrays as the syntax and special behavior of raw arrays tends to turn everything into a confusing mess in no time. In general, it might be worth to implement your own multi-dimensional array type, like an NDArray<int, 2, 3, 2> which internally works with a flattened representation and just maps multi-dimensional indices to a linear index. One advantage of this approach (besides the cleaner syntax) would be that you can easily change the mapping, e.g., to switch from row-major to column-major layout, e.g., for performance optimization…
To implement a general nD array with static dimensions, I would introduce a helper class to encapsulate the recursive computation of a linear index from an nD index:
template <std::size_t... D>
struct row_major;
template <std::size_t D_n>
struct row_major<D_n>
{
static constexpr std::size_t SIZE = D_n;
std::size_t operator ()(std::size_t i_n) const
{
return i_n;
}
};
template <std::size_t D_1, std::size_t... D_n>
struct row_major<D_1, D_n...> : private row_major<D_n...>
{
static constexpr std::size_t SIZE = D_1 * row_major<D_n...>::SIZE;
template <typename... Tail>
std::size_t operator ()(std::size_t i_1, Tail&&... tail) const
{
return i_1 + D_1 * row_major<D_n...>::operator ()(std::forward<Tail>(tail)...);
}
};
And then:
template <typename T, std::size_t... D>
class NDArray
{
using memory_layout_t = row_major<D...>;
T data[memory_layout_t::SIZE];
public:
template <typename... Args>
T& operator ()(Args&&... args)
{
memory_layout_t memory_layout;
return data[memory_layout(std::forward<Args>(args)...)];
}
};
NDArray<int, 2, 3, 5> arr;
int main()
{
int x = arr(1, 2, 3);
}

Is it possible to define a std::array without size? [duplicate]

(Note: This question is about not having to specify the number of elements and still allow nested types to be directly initialized.)
This question discusses the uses left for a C array like int arr[20];. On his answer, #James Kanze shows one of the last strongholds of C arrays, it's unique initialization characteristics:
int arr[] = { 1, 3, 3, 7, 0, 4, 2, 0, 3, 1, 4, 1, 5, 9 };
We don't have to specify the number of elements, hooray! Now iterate over it with the C++11 functions std::begin and std::end from <iterator> (or your own variants) and you never need to even think of its size.
Now, are there any (possibly TMP) ways to achieve the same with std::array? Use of macros allowed to make it look nicer. :)
??? std_array = { "here", "be", "elements" };
Edit: Intermediate version, compiled from various answers, looks like this:
#include <array>
#include <utility>
template<class T, class... Tail, class Elem = typename std::decay<T>::type>
std::array<Elem,1+sizeof...(Tail)> make_array(T&& head, Tail&&... values)
{
return { std::forward<T>(head), std::forward<Tail>(values)... };
}
// in code
auto std_array = make_array(1,2,3,4,5);
And employs all kind of cool C++11 stuff:
Variadic Templates
sizeof...
rvalue references
perfect forwarding
std::array, of course
uniform initialization
omitting the return type with uniform initialization
type inference (auto)
And an example can be found here.
However, as #Johannes points out in the comment on #Xaade's answer, you can't initialize nested types with such a function. Example:
struct A{ int a; int b; };
// C syntax
A arr[] = { {1,2}, {3,4} };
// using std::array
??? std_array = { {1,2}, {3,4} };
Also, the number of initializers is limited to the number of function and template arguments supported by the implementation.
Best I can think of is:
template<class T, class... Tail>
auto make_array(T head, Tail... tail) -> std::array<T, 1 + sizeof...(Tail)>
{
std::array<T, 1 + sizeof...(Tail)> a = { head, tail ... };
return a;
}
auto a = make_array(1, 2, 3);
However, this requires the compiler to do NRVO, and then also skip the copy of returned value (which is also legal but not required). In practice, I would expect any C++ compiler to be able to optimize that such that it's as fast as direct initialization.
I'd expect a simple make_array.
template<typename ret, typename... T> std::array<ret, sizeof...(T)> make_array(T&&... refs) {
// return std::array<ret, sizeof...(T)>{ { std::forward<T>(refs)... } };
return { std::forward<T>(refs)... };
}
Combining a few ideas from previous posts, here's a solution that works even for nested constructions (tested in GCC4.6):
template <typename T, typename ...Args>
std::array<T, sizeof...(Args) + 1> make_array(T && t, Args &&... args)
{
static_assert(all_same<T, Args...>::value, "make_array() requires all arguments to be of the same type."); // edited in
return std::array<T, sizeof...(Args) + 1>{ std::forward<T>(t), std::forward<Args>(args)...};
}
Strangely, can cannot make the return value an rvalue reference, that would not work for nested constructions. Anyway, here's a test:
auto q = make_array(make_array(make_array(std::string("Cat1"), std::string("Dog1")), make_array(std::string("Mouse1"), std::string("Rat1"))),
make_array(make_array(std::string("Cat2"), std::string("Dog2")), make_array(std::string("Mouse2"), std::string("Rat2"))),
make_array(make_array(std::string("Cat3"), std::string("Dog3")), make_array(std::string("Mouse3"), std::string("Rat3"))),
make_array(make_array(std::string("Cat4"), std::string("Dog4")), make_array(std::string("Mouse4"), std::string("Rat4")))
);
std::cout << q << std::endl;
// produces: [[[Cat1, Dog1], [Mouse1, Rat1]], [[Cat2, Dog2], [Mouse2, Rat2]], [[Cat3, Dog3], [Mouse3, Rat3]], [[Cat4, Dog4], [Mouse4, Rat4]]]
(For the last output I'm using my pretty-printer.)
Actually, let us improve the type safety of this construction. We definitely need all types to be the same. One way is to add a static assertion, which I've edited in above. The other way is to only enable make_array when the types are the same, like so:
template <typename T, typename ...Args>
typename std::enable_if<all_same<T, Args...>::value, std::array<T, sizeof...(Args) + 1>>::type
make_array(T && t, Args &&... args)
{
return std::array<T, sizeof...(Args) + 1> { std::forward<T>(t), std::forward<Args>(args)...};
}
Either way, you will need the variadic all_same<Args...> type trait. Here it is, generalizing from std::is_same<S, T> (note that decaying is important to allow mixing of T, T&, T const & etc.):
template <typename ...Args> struct all_same { static const bool value = false; };
template <typename S, typename T, typename ...Args> struct all_same<S, T, Args...>
{
static const bool value = std::is_same<typename std::decay<S>::type, typename std::decay<T>::type>::value && all_same<T, Args...>::value;
};
template <typename S, typename T> struct all_same<S, T>
{
static const bool value = std::is_same<typename std::decay<S>::type, typename std::decay<T>::type>::value;
};
template <typename T> struct all_same<T> { static const bool value = true; };
Note that make_array() returns by copy-of-temporary, which the compiler (with sufficient optimisation flags!) is allowed to treat as an rvalue or otherwise optimize away, and std::array is an aggregate type, so the compiler is free to pick the best possible construction method.
Finally, note that you cannot avoid copy/move construction when make_array sets up the initializer. So std::array<Foo,2> x{Foo(1), Foo(2)}; has no copy/move, but auto x = make_array(Foo(1), Foo(2)); has two copy/moves as the arguments are forwarded to make_array. I don't think you can improve on that, because you can't pass a variadic initializer list lexically to the helper and deduce type and size -- if the preprocessor had a sizeof... function for variadic arguments, perhaps that could be done, but not within the core language.
Using trailing return syntax make_array can be further simplified
#include <array>
#include <type_traits>
#include <utility>
template <typename... T>
auto make_array(T&&... t)
-> std::array<std::common_type_t<T...>, sizeof...(t)>
{
return {std::forward<T>(t)...};
}
int main()
{
auto arr = make_array(1, 2, 3, 4, 5);
return 0;
}
Unfortunatelly for aggregate classes it requires explicit type specification
/*
struct Foo
{
int a, b;
}; */
auto arr = make_array(Foo{1, 2}, Foo{3, 4}, Foo{5, 6});
EDIT No longer relevant:
In fact this make_array implementation is listed in sizeof... operator
The code below introduces undefined behavior as per [namespace.std]/4.4
4.4 The behavior of a C++ program is undefined if it declares a deduction guide for any standard library class template.
# c++17 version
Thanks to template argument deduction for class templates proposal we can use deduction guides to get rid of make_array helper
#include <array>
namespace std
{
template <typename... T> array(T... t)
-> array<std::common_type_t<T...>, sizeof...(t)>;
}
int main()
{
std::array a{1, 2, 3, 4};
return 0;
}
Compiled with -std=c++1z flag under x86-64 gcc 7.0
I know it's been quite some time since this question was asked, but I feel the existing answers still have some shortcomings, so I'd like to propose my slightly modified version. Following are the points that I think some existing answers are missing.
1. No need to rely on RVO
Some answers mention that we need to rely on RVO to return the constructed array. That is not true; we can make use of copy-list-initialization to guarantee there will never be temporaries created. So instead of:
return std::array<Type, …>{values};
we should do:
return {{values}};
2. Make make_array a constexpr function
This allow us to create compile-time constant arrays.
3. No need to check that all arguments are of the same type
First off, if they are not, the compiler will issue a warning or error anyway because list-initialization doesn't allow narrowing. Secondly, even if we really decide to do our own static_assert thing (perhaps to provide better error message), we should still probably compare the arguments' decayed types rather than raw types. For example,
volatile int a = 0;
const int& b = 1;
int&& c = 2;
auto arr = make_array<int>(a, b, c); // Will this work?
If we are simply static_asserting that a, b, and c have the same type, then this check will fail, but that probably isn't what we'd expect. Instead, we should compare their std::decay_t<T> types (which are all ints)).
4. Deduce the array value type by decaying the forwarded arguments
This is similar to point 3. Using the same code snippet, but don't specify the value type explicitly this time:
volatile int a = 0;
const int& b = 1;
int&& c = 2;
auto arr = make_array(a, b, c); // Will this work?
We probably want to make an array<int, 3>, but the implementations in the existing answers probably all fail to do that. What we can do is, instead of returning a std::array<T, …>, return a std::array<std::decay_t<T>, …>.
There is one disadvantage about this approach: we can't return an array of cv-qualified value type any more. But most of the time, instead of something like an array<const int, …>, we would use a const array<int, …> anyway. There is a trade-off, but I think a reasonable one. The C++17 std::make_optional also takes this approach:
template< class T >
constexpr std::optional<std::decay_t<T>> make_optional( T&& value );
Taking the above points into account, a full working implementation of make_array in C++14 looks like this:
#include <array>
#include <type_traits>
#include <utility>
template<typename T, typename... Ts>
constexpr std::array<std::decay_t<T>, 1 + sizeof... (Ts)>
make_array(T&& t, Ts&&... ts)
noexcept(noexcept(std::is_nothrow_constructible<
std::array<std::decay_t<T>, 1 + sizeof... (Ts)>, T&&, Ts&&...
>::value))
{
return {{std::forward<T>(t), std::forward<Ts>(ts)...}};
}
template<typename T>
constexpr std::array<std::decay_t<T>, 0> make_array() noexcept
{
return {};
}
Usage:
constexpr auto arr = make_array(make_array(1, 2),
make_array(3, 4));
static_assert(arr[1][1] == 4, "!");
C++11 will support this manner of initialization for (most?) std containers.
(Solution by #dyp)
Note: requires C++14 (std::index_sequence). Although one could implement std::index_sequence in C++11.
#include <iostream>
// ---
#include <array>
#include <utility>
template <typename T>
using c_array = T[];
template<typename T, size_t N, size_t... Indices>
constexpr auto make_array(T (&&src)[N], std::index_sequence<Indices...>) {
return std::array<T, N>{{ std::move(src[Indices])... }};
}
template<typename T, size_t N>
constexpr auto make_array(T (&&src)[N]) {
return make_array(std::move(src), std::make_index_sequence<N>{});
}
// ---
struct Point { int x, y; };
std::ostream& operator<< (std::ostream& os, const Point& p) {
return os << "(" << p.x << "," << p.y << ")";
}
int main() {
auto xs = make_array(c_array<Point>{{1,2}, {3,4}, {5,6}, {7,8}});
for (auto&& x : xs) {
std::cout << x << std::endl;
}
return 0;
}
С++17 compact implementation.
template <typename... T>
constexpr auto array_of(T&&... t) {
return std::array{ static_cast<std::common_type_t<T...>>(t)... };
}
While this answer is directed more towards this question, that question was marked as a duplicate of this question. Hence, this answer is posted here.
A particular use that I feel hasn't been fully covered is a situation where you want to obtain a std::array of chars initialized with a rather long string literal but don't want to blow up the enclosing function. There are a couple of ways to go about this.
The following works but requires us to explicitly specify the size of the string literal. This is what we're trying to avoid:
auto const arr = std::array<char const, 12>{"some string"};
One might expect the following to produce the desired result:
auto const arr = std::array{"some string"};
No need to explicitly specify the size of the array during initialization due to template deduction. However, this wont work because arr is now of type std::array<const char*, 1>.
A neat way to go about this is to simply write a new deduction guide for std::array. But keep in mind that some other code could depend on the default behavior of the std::array deduction guide.
namespace std {
template<typename T, auto N>
array(T (&)[N]) -> array<T, N>;
}
With this deduction guide std::array{"some string"}; will be of type std::array<const char, 12>. It is now possible to initialize arr with a string literal that is defined somewhere else without having to specify its size:
namespace {
constexpr auto some_string = std::array{"some string"};
}
auto func() {
auto const arr = some_string;
// ...
}
Alright, but what if we need a modifiable buffer and we want to initialize it with a string literal without specifying its size?
A hacky solution would be to simply apply the std::remove_cv type trait to our new deduction guide. This is not recommended because this will lead to rather surprising results. String literals are of type const char[], so it's expected that our deduction guide attempts to match that.
It seems that a helper function is necessary in this case. With the use of the constexpr specifier, the following function can be executed at compile time:
#include <array>
#include <type_traits>
template<typename T, auto N>
constexpr auto make_buffer(T (&src)[N]) noexcept {
auto tmp = std::array<std::remove_cv_t<T>, N>{};
for (auto idx = decltype(N){}; idx < N; ++idx) {
tmp[idx] = src[idx];
}
return tmp;
}
Making it possible to initialize modifiable std::array-like buffers as such:
namespace {
constexpr auto some_string = make_buffer("some string");
}
auto func() {
auto buff = some_string;
// ...
}
And with C++20, the helper function can even be simplified:
#include <algorithm>
#include <array>
#include <type_traits>
template<typename T, auto N>
constexpr auto make_buffer(T (&src)[N]) noexcept {
std::array<std::remove_cv_t<T>, N> tmp;
std::copy(std::begin(src), std::end(src), std::begin(tmp));
return tmp;
}
C++20 UPDATE: Although there are some excellent answers that provide the desired functionality (such as Gabriel Garcia's answer that uses std::index_sequence), I am adding this answer because the simplest way to do this as of C++20 isn't mentioned: just use std::to_array(). Using the OP's last example of an array of structs:
struct A{ int a; int b; };
// C syntax
A arr[] = { {1,2}, {3,4} };
// using std::array
auto std_array = std::to_array<A>({ {1,2}, {3,4} });
If std::array is not a constraint and if you have Boost, then take a look at list_of(). This is not exactly like C type array initialization that you want. But close.
Create an array maker type.
It overloads operator, to generate an expression template chaining each element to the previous via references.
Add a finish free function that takes the array maker and generates an array directly from the chain of references.
The syntax should look something like this:
auto arr = finish( make_array<T>->* 1,2,3,4,5 );
It does not permit {} based construction, as only operator= does. If you are willing to use = we can get it to work:
auto arr = finish( make_array<T>= {1}={2}={3}={4}={5} );
or
auto arr = finish( make_array<T>[{1}][{2}[]{3}][{4}][{5}] );
None of these look like good solutions.
Using variardics limits you to your compiler-imposed limit on number of varargs and blocks recursive use of {} for substructures.
In the end, there really isn't a good solution.
What I do is I write my code so it consumes both T[] and std::array data agnostically -- it doesn't care which I feed it. Sometimes this means my forwarding code has to carefully turn [] arrays into std::arrays transparently.
None of the template approaches worked properly for me for arrays of structs, so I crafted this macro solution:
#define make_array(T, ...) \
(std::array<T,sizeof((T[]){ __VA_ARGS__ })/sizeof(T)> {{ __VA_ARGS__ }})
auto a = make_array(int, 1, 2, 3);
struct Foo { int x, y; };
auto b = make_array(Foo,
{ 1, 2 },
{ 3, 4 },
{ 5, 6 },
);
Note that although the macro expands its array arguments twice, the first time is inside sizeof, so any side effects in the expression will correctly happen only once.
Have fun!

C++ template metafunction on instantiated object of a template class

I hope that this question isn't overly convoluted. I realize that meta-programming acts on types rather than on the objects of those types; however, I am still trying to achieve the same result, by 1) retrieving the type information from the class and then 2) meta-functions on that type information.
An explanation of my situation is as follows with simplified code excerpts:
I have a template class for matrices, which I am calling Matrix_Base. Somewhat similar to the approach taken by Eigen, I am allowing for two possibilities for the size of a matrix -- either fixed at compile-time or fixed at run-time. Simplified declaration of Matrix_Base is:
template <typename Type, uint32_t rows_ = 1, uint32_t cols_ = 1,>
class Matrix_Base{
/*
...
*/
};
Run-time sized matrix is denoted by an argument of 0.
The check for run-time vs compile-time sizing of the matrix is fairly simple (using boost::mpl):
typedef typename mpl::if_<
typename mpl::or_<
typename mpl::equal_to<
typename mpl::int_<rows_>::type,
mpl::int_<0>
>::type,
typename mpl::equal_to <
typename mpl::int_<cols_>::type,
mpl::int_<0>
>
>::type,
mpl::true_,
mpl::false_
>::type runtime_size_type;
This has been tested and works fine. My trouble starts about here...
Presently, I am using the above boost::mpl code in the following manner:
namespace internal {
template <uint32_t rows = 1, uint32_t cols_ = 1>
struct Runtime_Size_Helper {
typedef typename mpl::if_<
// REST OF THE BOOST::MPL code here //
>::type runtime_size_t
bool value() { return runtime_size_t::value;}
};
} // namespace
template <typename Type, uint32_t rows_ = 1, uint32_t cols_ = 1>
class Matrix_Base{
// ...
static constexpr uint32_t rows = rows_;
static constexpr uint32_t cols = cols_;
bool is_runtime_sized;
// ...
};
template <typename T, uint32_t R, uint32_t C>
bool Matrix_Base<T,R,C>::is_runtime_sized = internal::Runtime_Size_Helper<R,C>::value();
This makes the result of that mpl function into a member of the Matrix_Base class. So far so good.
I'd like to use some form of indirection to determine the value of runtime_size_type by passing it the instantiated object. As per the example code, the only required information to determine this is the uint32_t parameters for col and row.
For the instantiated Matrix_Base object, the relevant information will never change from its compile-type values. The size of the matrix will be immutable; the size will either be set from the template arguments or -- for runtime-sized matrices -- through the constructor. In both cases, the template arguments are fixed and part of the type information. I am saving this information as static variables in the class, and I have even tried to add a typedef with all of the template parameters as my_type typename typename Matrix_Base<T,rows_, cols_, ...> my_type but I cannot seem to figure out how to write a metafunction to which I can pass a Matrix_Base object (obviously as a reference or pointer) and re-extract the relevant information.
I am fully open to incorporating (other) boost libraries, if they would provide the necessary functionality.
Hope that this is clear. Please let me know if there's something here that's unclear or that's just plain stupid.
Best regards,
Shmuel
edited the text to provide a bit more clarity as to the issue
The quickest way to do what you seem to want (you didn't really specify it in detail) is to let your matrix class template inherit from a storage class template, and to specialize the storage class depending on whether your MPL predicate returns true or false
#include <array>
#include <iostream>
#include <vector>
#include <boost/mpl/bool.hpp>
#include <boost/mpl/comparison.hpp>
#include <boost/mpl/if.hpp>
#include <boost/mpl/int.hpp>
#include <boost/mpl/logical.hpp>
using namespace boost;
// in C++98, use
// template<int R, int C>
// struct is_runtime_sized: mpl::if<
// ...
// >::type {};
template<int R, int C>
using is_runtime_sized = typename mpl::if_<
mpl::or_<
mpl::equal_to<mpl::int_<R>, mpl::int_<0>>,
mpl::equal_to<mpl::int_<C>, mpl::int_<0>>
>,
mpl::true_, mpl::false_
>::type;
Note that I've eleminated some unnecessary typename occurances to make the MPL predicate more readable.
template<class T, int R, int C, bool = is_runtime_sized<R, C>::value>
struct MatrixStorage
{
MatrixStorage() = default;
MatrixStorage(int r, int c): data_(r * c) {} // zero-initializes
protected:
std::vector<T> data_;
};
template<class T, int R, int C>
struct MatrixStorage<T, R, C, false>
{
MatrixStorage() = default;
MatrixStorage(int, int): data_{} {} // zero-initializes
protected:
std::array<T, R * C> data_;
};
Here, I've split the implementation of the dynamically and statically stored matrices. The former uses a std::vector and the latter a std:array. Similar to Eigen, both have a default constructor, and both also have a constructor taking the matrix dimensions that zero-initializes.
template<class T, int R = 0, int C = 0>
struct Matrix: public MatrixStorage<T, R, C>
{
Matrix() = default;
// In C++98, write:
// Matrix(int r, int c): MatrixStorage<T, R, C>(r, c) {}
using MatrixStorage<T, R, C>::MatrixStorage;
int size() const { return this->data_.size(); }
};
The actual Matrix class inherits from the MatrixStorage, and returns a size() depending on the currently stored data.
int main()
{
Matrix<int> m_dyn(3, 3);
std::cout << m_dyn.size() << "\n"; // 9
Matrix<int, 2, 2> m_stat;
std::cout << m_stat.size() << "\n"; // 4
}
Live Example. As you can see, the dynamically allocated matrix has size 9, and the statically sized one has size 4. Note that there are two small C++11 features in the above code, that you can easily work-around if you are required to use C++11.