Deduce std::array size?
C++17 std::array
class template argument deduction (CTAD)
Starting with C++17, this new language feature is now used by the standard library and now allows us to omit the template types as well so that the following works:
main.cpp
#include <array>
int main() {
std::array a{1, 2, 3};
}
instead of std::array<int, 3> a{1, 2, 3};
Tested with:
g++ -ggdb3 -O0 -std=c++17 -Wall -Wextra -pedantic -o main.out main.cpp
If we set -std=c++14
instead for example, it fails to compile with:
error: missing template arguments before ‘a’
Tested on Ubuntu 18.04, GCC 7.5.0.
How come std::initializer_list is allowed to not specify size AND be stack allocated at the same time?
The thing is, std::initializer_list
does not hold the objects inside itself. When you instantiate it, compiler injects some additional code to create a temporary array on the stack and stores pointers to that array inside the initializer_list. For what its worth, an initializer_list is nothing but a struct with two pointers (or a pointer and a size):
template <class T>
class initializer_list {
private:
T* begin_;
T* end_;
public:
size_t size() const { return end_ - begin_; }
T const* begin() const { return begin_; }
T const* end() const { return end_; }
// ...
};
When you do:
foo({2, 3, 4, 5, 6});
Conceptually, here is what is happening:
int __tmp_arr[5] {2, 3, 4, 5, 6};
foo(std::initializer_list{arr, arr + 5});
One minor difference being, the life-time of the array does not exceed that of the initializer_list.
How to initialize all members of an array to the same value?
Unless that value is 0 (in which case you can omit some part of the initializer
and the corresponding elements will be initialized to 0), there's no easy way.
Don't overlook the obvious solution, though:
int myArray[10] = { 5, 5, 5, 5, 5, 5, 5, 5, 5, 5 };
Elements with missing values will be initialized to 0:
int myArray[10] = { 1, 2 }; // initialize to 1,2,0,0,0...
So this will initialize all elements to 0:
int myArray[10] = { 0 }; // all elements 0
In C++, an empty initialization list will also initialize every element to 0.
This is not allowed with C until C23:
int myArray[10] = {}; // all elements 0 in C++ and C23
Remember that objects with static storage duration will initialize to 0 if no
initializer is specified:
static int myArray[10]; // all elements 0
And that "0" doesn't necessarily mean "all-bits-zero", so using the above is
better and more portable than memset(). (Floating point values will be
initialized to +0, pointers to null value, etc.)
C++11: Correct std::array initialization?
This is the bare implementation of std::array
:
template<typename T, std::size_t N>
struct array {
T __array_impl[N];
};
It's an aggregate struct whose only data member is a traditional array, such that the inner {}
is used to initialize the inner array.
Brace elision is allowed in certain cases with aggregate initialization (but usually not recommended) and so only one brace can be used in this case. See here: C++ vector of arrays
How to initialize std::array member in constructor initialization list when the size of the array is a template parameter
std::index_sequence
was provided in order to simplify the metaprogramming task of creating and expanding a pack whose size is not fixed. Use std::make_index_sequence
and delegate the construction to some private constructor that deduces the pack:
A(H h) : A(h, std::make_index_sequence<N>{}) {}
template <std::size_t... i>
A(H h, std::index_sequence<i...>) : hashes{((void)i, h)...} {}
Here, ((void)i, h)
is a comma expression that evaluates to h
, but since we mentioned the pack i
inside it, it's eligible to be expanded using ...
. The result is N
copies of h
(one for each element of i
). This expansion occurs inside a braced-init-list, so the result is a braced-init-list contaning N
copies of h
, which will then initialize the hashes
member.
How to initialize a class like std::array
std::array is an aggregate and so it uses aggregate initialization. Providing excess elements during aggregate initialization is ill-formed and requires a diagnostic. The compiler at minimum has to provide a warning, both gcc and clang make this an error. So if you make your class an aggregate then you can have it work the same way std::array does. Note, in class member initializers makes your class a non-aggregate in C++11 but not in C++14.
We can see it is an aggregate by going to the draft C++11 standard section 23.3.2.1
[array.overview]:
An array is an aggregate (8.5.1) that can be initialized with the
syntaxarray<T, N> a = { initializer-list };
where initializer-list is a comma-separated list of up to N elements
whose types are convertible to T.
and section 8.5.1
[dcl.init.aggr] covers aggregate intialization and says:
An initializer-list is ill-formed if the number of initializer-clauses
exceeds the number of members or elements to initialize.
The draft standard provides for exposition a possible implementation which shortened to the bare minimum looks like this:
template <class T, size_t N>
struct array {
T elems[N];
};
and the draft standard has a note which says:
The member variable elems is shown for exposition only, to emphasize that array is a class aggregate.
The name elems is not part of array’s interface
which is an aggregate and will with both gcc and clang provide an error if excess elements are provided.
Also see What are Aggregates and PODs and how/why are they special?.
How can I initialize an array in C++ with a size that is given to the constructor?
std::vector
is the best match here. (As you said, in most cases raw arrays and pointers could be avoided. Also see How can I efficiently select a Standard Library container in C++11?)
And you can initialize the data member directly in member initializer list instead of assigning in the constructor body after default-initialization, e.g.
Obj(int xsize, int ysize) : array(xsize, std::vector<int>(ysize, 0)) {
}
Related Topics
How Similar Are Boost.Filesystem and the C++ Standard Filesystem Library
Does C and C++ Guarantee the Ascii of [A-F] and [A-F] Characters
When to Use Functors Over Lambdas
C++ Boost Asio Simple Periodic Timer
Difference Between Logical and Physical Const-Ness
Making a C++ Class a Monitor (In the Concurrent Sense)
Template Metaprogramming - Difference Between Using Enum Hack and Static Const
How to Enable_Shared_From_This of Both Parent and Derived
Win32: How to Hide 3Rd Party Windows in Taskbar by Hwnd
Extracting 3D Coordinates Given 2D Image Points, Depth Map and Camera Calibration Matrices
Lambda Capture and Parameter with Same Name - Who Shadows the Other? (Clang VS Gcc)
Creating Array of Objects on the Stack and Heap
Safer But Easy-To-Use and Flexible C++ Alternative to Sscanf()