I am confused about the output in the following program about the vec in Test. Why it's a vector with size 100 instead of 1? I thought std::vector<T> var{a} is the same as std::vector<T> var = {a}.
#include <iostream>
#include <vector>
using namespace std;
struct Value {
int a;
int b;
};
class Test {
public:
std::vector<struct Value> vec{100};
};
class Test2 {
public:
std::vector<int> vec{100};
};
int main()
{
Test test;
std::cout << "test size: " << test.vec.size() << std::endl;
Test2 test2;
std::cout << "test2 size: " << test2.vec.size();
return 0;
}
Output:
test size: 100
test2 size: 1
std::vector has a constructor with a std::initializer_list<T> argument. When using an initializer list like {100} this constructor will always take priority, if it is applicable.
For a std::vector<int> the initializer {100} is compatible with std::initializer_list<int> so that constructor will be used. It will create a vector containing the collection {100} which is a single int with the value 100.
For std::vector<Value> the initializer {100} is NOT compatible with a std::initializer_list<Value> argument. Value has no converting constructor for turning an int to a Value so you cannot construct a std::initializer_list<Value> from {100}. You can verify that this conversion is not allowed with this example. The compiler will try to take a lower priority constructor instead, and will use the constructor which initializes 100 default constructed Value.
If you add a Value::Value(int) constructor or use {{100}} as the initializer for Test2 you will find that the std::vector<Value> will now only contain a single element. In both cases, the initializer list is convertible to std::initializer_list<Value> and that constructor will now be used.
As you discovered the meaning of {100}, changes for T == int.
To answer your question briefly:
The 100 in vector<Value>{100} cannot be interpreted as a Value and therefore the size constructor takes precedence.
If you insist, {100} can be interpreted as Value, so you may need an extra curly braces, vector<Value>{ {100} }.
See the illustration here: https://godbolt.org/z/xcMT1oc5z
My advice, avoiding further discussion on legalities, is the following:
To keep the meaning across types, initialize consistently parenthesis for size-initialization and brackets for element(s), which forces you to do this:
std::vector<int> vec = std::vector<int>(100);
And in general:
std::vector<T> vec = std::vector<T>(100);
In this case 100 is always the size.
This may not be a totally helpful answer, but I decided to put a breakpoint in the class declaration for vector in the STD library.
Answer
In the definition of a vector, there are 3 ways it handles the assignment.
A struct will provide a value-construction, where as an int will be assigned as a sized range construction.
It reads std::vector<struct> vect{100}; as building a vector of length 100, while std::vector<int> vect{100}; is acting similarly vect.insert(v.end(),100);
This is based on the type of object passed in for .
For information's sake, the final option is taking a given value, and assigning it to a number of spots. So if you had 100, "x", it would put "x" into your vector 100 times.
The Journey
What I learned from this is that there's a point where your vector takes a size_type input and a _Valty&& input (which I don't know what that is yet. Will be looking it up later) and provides a construction between 3 different args.
My best guess would be that your struct is filling in for 1-args path and acts as a length declaration, while int as a native type falls into the 2-args path and acts as a value assignment.
The sizeof(Value) may == 0, while the size of an int will be 1.
Edit: I guessed 1 and 2 (or _Count == 0, and Count == 1), however I was wrong about this. It's _Count == 0 and _Count == 2. Which was very interesting.
template <class... _Valty>
_CONSTEXPR20 void _Construct_n(_CRT_GUARDOVERFLOW const size_type _Count, _Valty&&... _Val) {
// Dispatches between the three sized constructions.
// 1-arg -> value-construction, e.g. vector(5)
// 2-arg -> fill, e.g. vector(5, "meow")
// 3-arg -> sized range construction, e.g. vector{"Hello", "Fluffy", "World"}
auto& _Al = _Getal(); //////////////// For test1, _Count is 100, for test2, _Count is 1;
auto&& _Alproxy = _GET_PROXY_ALLOCATOR(_Alty, _Al);
auto& _My_data = _Mypair._Myval2;
_Container_proxy_ptr<_Alty> _Proxy(_Alproxy, _My_data);
if (_Count != 0) {
_Buy_nonzero(_Count);
_Tidy_guard<vector> _Guard{this};
// This one happens with a struct
if constexpr (sizeof...(_Val) == 0) {
_My_data._Mylast = _Uninitialized_value_construct_n(_My_data._Myfirst, _Count, _Al);
} else
if constexpr (sizeof...(_Val) == 1) {
_STL_INTERNAL_STATIC_ASSERT(is_same_v<_Valty..., const _Ty&>);
_My_data._Mylast = _Uninitialized_fill_n(_My_data._Myfirst, _Count, _Val..., _Al);
} else
// This one happens with an int
if constexpr (sizeof...(_Val) == 2) {
_My_data._Mylast = _Uninitialized_copy(_STD forward<_Valty>(_Val)..., _My_data._Myfirst, _Al);
} else {
static_assert(_Always_false<_Ty>, "Should be unreachable");
}
_Guard._Target = nullptr;
}
_Proxy._Release();
}
What's really interesting as that it appears to be happening when the Allocator reference is assigned. I'm by no means an expert in the language, but I really wanted to figure this puzzle one! Thanks for the interesting challenge!
If you've never walked through a class definition before, I would recommend trying it out.
Related
If I've got a constructor with an initialization list as such:
std::vector<int> _list;
Program::Program() : _list(0)
{
}
Does this initialize all values to 0 even when the vector resizes itself?
You seem to be misunderstanding the argument of the vector constructor that you're calling. _list(0) will initialise _list to length zero, using the default value for type int, which also happens to be zero, but which is irrelevant if the vector doesn't contain any elements. It doesn't explicitly set element values to zero. Perhaps you meant to have the constructor repeats a single value a specified number of times? If so, you need to pass the desired length as the first argument, and the value to repeat for its second argument. This construction does not affect an subsequent resizing to expand the vector, which will populate the new vector elements with the default value (or a different value if you specify one as an additional argument to vector::resize).
Here's an example to illustrate, based on your code, which first initialises the vector with the value 10 repeating for length 5, and then resizes the vector to length 10.
#include <iostream>
#include <vector>
class Program
{
public:
Program() : _list(0) { }
Program(unsigned long size, int value) : _list(size, value) { }
void ResizeList(unsigned long size)
{
_list.resize(size);
}
void PrintList() const
{
std::cout << "_list = ";
for (const auto& val : _list)
{
std::cout << val << ", ";
}
std::cout << std::endl;
}
private:
std::vector<int> _list;
};
int main()
{
Program p(5, 10);
p.PrintList();
p.ResizeList(10);
p.PrintList();
return 0;
}
Output:
_list = 10, 10, 10, 10, 10,
_list = 10, 10, 10, 10, 10, 0, 0, 0, 0, 0,
If you look at the documentation for the std::vector constructor, you'll see that for constructor (3), the one you're using, you'll see that you're constructing 0 elements of type int in-place in _list. This means that you're essentially doing nothing.
When the vector is resized, the elements that space is allocated for will be uninitialized, unless you use the resize function, in which case the elements will be initialized to their default value, or a value of your choice.
For example, if your vector were empty and you did _list.resize(10);, _list would now contain 10 elements of the default-constructed type of int, which should just be 0. If you instead did something like _list.resize(10, 5);, _list would now contain 10 5s.
I hope this helped clear things up for you. If you have any follow up questions, feel free to ask.
I have classes in the style of Class1 (see code). An enum and a function to get all the values from the enum. The values (FOO_1, FOO_2 etc) differ from Class to Class as well as the number of values (sizeof(Foos)).
I call the function once to get the sizeof the enum, reserve memory and with the second call I want to get all the values to *pFoos (2,1,6 in the sample code).
Is there a better way then using an array with all the values in it (size_t arr[3] ={FOO_1 , FOO_X, FOO_BAR })?
class Class1{
enum Foos{
FOO_1 = 2,
FOO_X = 1,
FOO_BAR = 6
}
};
Class1::GetFoos(size_t* pFoos, size_t* pSize)
{
size_t len = sizeof(Foos);
if (len > *pSize)
{ //Call function once to get the size
*pSize= len ;
return -1;
}
for(size_t i = 0; i< *pSize; i++)
{
//copy all enum values to pFoos
}
};
Disclaimer: shameless plug – I am the author.
Reflective enums are possible in C++. I wrote a header-only library that captures a bunch of "patterns" at compile time and gives you syntax like this:
ENUM(Class1, int, FOO_1 = 2, FOO_X = 1, FOO_BAR = 6)
size_t count = Class1::_size;
for (size_t index = 0; index < Class1::_size; ++index)
do_anything(Class1::_values()[index]);
What it does internally is use the macro to generate an array of the values that you have declared, kind of like in your question, and use a bunch of other tricks to allow you to use initializers naturally. It then provides iterators and other things on top of the array.
Here is a link: https://github.com/aantron/better-enums
EDIT – internals
Here is a pseudocode sketch of what it does internally. The reason I am only giving a "sketch" is because there are a bunch of issues to consider when doing this portably. I will touch on all the most important elements.
ENUM(Class1, int, FOO_1 = 2, FOO_X = 1, FOO_BAR = 6)
notionally expands to
struct Class1 {
enum _enumerated { FOO_1 = 2, FOO_X = 1, FOO_BAR = 6 };
// Fairly obvious methods for how to iterate over _values and
// _names go here. Iterators are simply pointers into _values
// and _names below.
static size_t _size = sizeof(_values) / sizeof(int);
int _value;
};
int _values[] = {(fix_t<Class1>)Class1::FOO_1 = 2,
(fix_t<Class1>)Class1::FOO_X = 1,
(fix_t<Class1>)Class1::FOO_BAR = 6};
const char *_names[] = {"FOO_1 = 2", "FOO_X = 1", "FOO_BAR = 6"};
This is done by using variadic macros and stringization. The methods that deal with strings treat not only \0, but also space and equals as terminators, which allows them to ignore the initializers in the stringized constants that you see in _names.
The type fix_t is necessary because having assignments inside an array initializer is not valid C++. What that type does is take on the value of the enum, then ignore the assignment by an overloaded assignment operator, and then return the original value. A sketch:
template <typename Enum>
struct fix_t {
Enum _value;
fix_t(Enum value) : _value(value) { }
const fix_t& operator =(int anything) const { return *this; }
operator Enum() const { return _value; }
};
This makes the _values array possible declare even in the presence of initializers.
Of course, these arrays need to be prefixed so that you can have more than one enum like this. They also need to have the same as "extern inline" linkage for functions, so that they are shared between multiple compilation units.
Until c++ will get reflection you will not get any data from your enum! Simply you can not get "all" values from an enum. A enum is simply a kind of namespace where some constants can be defined and may be enumerated automatically. Not more at all. You have no text representation, no count information, no value to text information!
Is there a better way then using an array with all the values in it (size_t arr[3] ={FOO_1 , FOO_X, FOO_BAR })?
If you're tagging the question as C++ I advise you to give up with the C way of doing things, so the better way to do this in C++ is using a std::vector:
class Class1{
enum Foos{
FOO_1 = 2,
FOO_X = 1,
FOO_BAR = 6
};
public:
std::vector<int> GetFoos()
{
// return all enum values
return {FOO_1, FOO_X, FOO_BAR};
}
};
You can use it this way:
Class1 c1;
auto foos = c1.GetFoos();
std::cout << "I have " << c1.size() << " foos:\n";
for (const auto &foo : foos) std::cout << foo << '\n';
If you don't want to create the vector at runtime, you can create it once declaring it static:
class Alpha{
enum Alphas{
BETA = 0b101010,
GAMMA = 0x20,
EPSILON = 050
};
static const std::vector<int> m_alphas;
public:
const std::vector<int> &GetAlphas()
{
return m_alphas;
}
};
// https://isocpp.org/wiki/faq/ctors#explicit-define-static-data-mems
const std::vector<int> Alpha::m_alphas = {BETA, GAMMA, EPSILON};
Live demo
I know that is a burden to maintain but since there's no way to iterate the values of an enum, all the code that tries to iterate them is a burden as well.
Maybe in the following answer you can find something useful to iterate enums in a better way for your goals:
enum to string in modern C++ and future C++17.
I was coding up a Union find data structure , and was trying to initialize the parent vector with a value parent[i]=i, Is there a way in c++ to initialize the vector like this , that is declaring a vector of size N , and not assigning fixed values to each element, rather position dependent value to each element. (without using any obvious for loops)
This is what I was looking for:
std::vector<int> parent(Initializer);
where Initializer is some class or a function.
To try out my hand a bit, I wrote this:
#include <iostream>
#include <vector>
using namespace std;
class Initializer {
private:
static int i;
public:
int operator() ()
{
return i++;
}
};
int main()
{
vector<int> parent(Initializer);
cout << parent[0];
return 0;
}
However I think I have messed up my concepts pretty bad here, and I am not getting what the declaration means, or what it is doing.
Please answer both the questions,
(1) How to initialize a vector with variable initial values.
(2) What exactly is the code I wrote doing?
This is a function declaration:
vector<int> parent(Initializer);
Becasue Initializer is a type name, you declared a function parent that takes Initializer as a (unnamed) parameter and returns vector<int>. See Most vexing parse.
To do what you want, you can do this:
std::vector<int> parent(N); // where N is the size you want
std::iota(parent.begin(), parent.end(), 0); // fill it with consecutive values
// starting with 0
There's std::generate algorithm that you can use to save result of a function (or function object) in a range:
std::generate(parent.begin(), parent.end(), Initializer());
Live demo.
There are several alternatives. If you want to initialize the vector with increasing values, then you can use std::iota.
std::vector<int> vec(size);
std::iota(std::begin(vec), std::end(vec), 0);
If you want something more general you could use std::generate.
std::vector<int> vec(size);
int n = 0;
std::generate(std::begin(vec), std::end(vec), [&n]() {return n++;});
I am searching for some help in next situation:
I have some class and some method in it, syntax is like this:
class SomeClass {
public:
void doSomething(int *a);
};
So I want to call this method like
SomeClass::doSomething({ 0, 1, 2, 3, 4 });
Is it possible in any language?
Any (C++, C, obj-c, obj-c++) implementation is welcome!
I know that this initialization block is a body of array, like
int *a = { 0, 1, 2, 3, 4 };
SomeClass::doSomething(a);
But interface will look great, I think, if there will be no temp variables before function calls (as we don't need to know the type of parameter in class-client). So, is there any chance to make this?
In C99 this works:
functionThatTakesIntPtrOrArray( (int []){ 1, 2, 3, 4 } );
..and similar things can be done with structs.
This is about C++11 initializer lists (section 18.9).
void foo (std :: initializer_list <int> inputs) {
for (auto i : inputs) {
// ...
}
}
foo ({10, 20, 30});
Only the compiler can create an initializer list, but you can treat it like a standard STL-style container with begin(), end(), size(), and random-access iterators.
std::vector (and I expect some other containers) can now be constructed with initializer lists, so
std :: vector <std :: string> foo {"a", "b"};
is equivalent to
std :: vector <std :: string> foo;
foo .push_back ("a");
foo .push_back ("b");
except that it may perform fewer allocations. Note that the const char* have been turned into std::string automagically.
If initializer_list is not available, and the arrays are mostly small, there is another option, overloading << operator for std::vector like this:
template <typename T>
inline std::vector<T> operator <<( const std::vector<T>& vec, const T& val ) {
std::vector<T> result(vec);
result.push_back(val);
return result;
}
With that, you can do this:
void foo (const std::vector<int>& inputs) {
// ...
}
foo ( std::vector<int>() << 10 << 20 << 30 );
There is a price to pay for this convenience of single line initialization and not having to specify vector size. A copy of the prior vector is created for every element that is added, making running time at least quadratic in vector size -- that's why this is best suited for short vectors and cases when performance does not matter. There is a better solution for C++11, as pointed out in spraff's answer.
one can initialize a temporary array as a function parameter like so
// onboard LED
const byte LED_RED = 36;
const byte LED_BLUE = 34;
const byte LED_GREEN = 35;
void turnOffLedAll(){
// TURN OFF ALL LED
digitalWrite(LED_RED, LOW);
digitalWrite(LED_BLUE, LOW);
digitalWrite(LED_GREEN, LOW);
}
void statusLED(byte led[], byte stat, byte time=0) {
turnOffLedAll();
// turn ON LED
for(int i=0; i<(sizeof(led)/sizeof(led[0])); i++){
digitalWrite(led[i], stat);
}
if (time>0){
delay(time*1000);
turnOffLedAll();
}
}
Considering the functions above an inline array initialization as a function parameter can be done like this:
statusLED((byte*)(const byte[]){LED_RED}, LOW, 0);
C++ Notes: Array Initialization has a nice list over initialization of arrays. I have a
int array[100] = {-1};
expecting it to be full with -1's but its not, only first value is and the rest are 0's mixed with random values.
The code
int array[100] = {0};
works just fine and sets each element to 0.
What am I missing here.. Can't one initialize it if the value isn't zero ?
And 2: Is the default initialization (as above) faster than the usual loop through the whole array and assign a value or does it do the same thing?
Using the syntax that you used,
int array[100] = {-1};
says "set the first element to -1 and the rest to 0" since all omitted elements are set to 0.
In C++, to set them all to -1, you can use something like std::fill_n (from <algorithm>):
std::fill_n(array, 100, -1);
In portable C, you have to roll your own loop. There are compiler-extensions or you can depend on implementation-defined behavior as a shortcut if that's acceptable.
There is an extension to the gcc compiler which allows the syntax:
int array[100] = { [0 ... 99] = -1 };
This would set all of the elements to -1.
This is known as "Designated Initializers" see here for further information.
Note this isn't implemented for the gcc c++ compiler.
The page you linked to already gave the answer to the first part:
If an explicit array size is
specified, but an shorter
initiliazation list is specified, the
unspecified elements are set to zero.
There is no built-in way to initialize the entire array to some non-zero value.
As for which is faster, the usual rule applies: "The method that gives the compiler the most freedom is probably faster".
int array[100] = {0};
simply tells the compiler "set these 100 ints to zero", which the compiler can optimize freely.
for (int i = 0; i < 100; ++i){
array[i] = 0;
}
is a lot more specific. It tells the compiler to create an iteration variable i, it tells it the order in which the elements should be initialized, and so on. Of course, the compiler is likely to optimize that away, but the point is that here you are overspecifying the problem, forcing the compiler to work harder to get to the same result.
Finally, if you want to set the array to a non-zero value, you should (in C++, at least) use std::fill:
std::fill(array, array+100, 42); // sets every value in the array to 42
Again, you could do the same with an array, but this is more concise, and gives the compiler more freedom. You're just saying that you want the entire array filled with the value 42. You don't say anything about in which order it should be done, or anything else.
C++11 has another (imperfect) option:
std::array<int, 100> a;
a.fill(-1);
With {} you assign the elements as they are declared; the rest is initialized with 0.
If there is no = {} to initalize, the content is undefined.
Using std::array, we can do this in a fairly straightforward way in C++14. It is possible to do in C++11 only, but slightly more complicated.
Our interface is a compile-time size and a default value.
template<typename T>
constexpr auto make_array_n(std::integral_constant<std::size_t, 0>, T &&) {
return std::array<std::decay_t<T>, 0>{};
}
template<std::size_t size, typename T>
constexpr auto make_array_n(std::integral_constant<std::size_t, size>, T && value) {
return detail::make_array_n_impl<size>(std::forward<T>(value), std::make_index_sequence<size - 1>{});
}
template<std::size_t size, typename T>
constexpr auto make_array_n(T && value) {
return make_array_n(std::integral_constant<std::size_t, size>{}, std::forward<T>(value));
}
The third function is mainly for convenience, so the user does not have to construct a std::integral_constant<std::size_t, size> themselves, as that is a pretty wordy construction. The real work is done by one of the first two functions.
The first overload is pretty straightforward: It constructs a std::array of size 0. There is no copying necessary, we just construct it.
The second overload is a little trickier. It forwards along the value it got as the source, and it also constructs an instance of make_index_sequence and just calls some other implementation function. What does that function look like?
namespace detail {
template<std::size_t size, typename T, std::size_t... indexes>
constexpr auto make_array_n_impl(T && value, std::index_sequence<indexes...>) {
// Use the comma operator to expand the variadic pack
// Move the last element in if possible. Order of evaluation is well-defined
// for aggregate initialization, so there is no risk of copy-after-move
return std::array<std::decay_t<T>, size>{ (static_cast<void>(indexes), value)..., std::forward<T>(value) };
}
} // namespace detail
This constructs the first size - 1 arguments by copying the value we passed in. Here, we use our variadic parameter pack indexes just as something to expand. There are size - 1 entries in that pack (as we specified in the construction of make_index_sequence), and they have values of 0, 1, 2, 3, ..., size - 2. However, we do not care about the values (so we cast it to void, to silence any compiler warnings). Parameter pack expansion expands out our code to something like this (assuming size == 4):
return std::array<std::decay_t<T>, 4>{ (static_cast<void>(0), value), (static_cast<void>(1), value), (static_cast<void>(2), value), std::forward<T>(value) };
We use those parentheses to ensure that the variadic pack expansion ... expands what we want, and also to ensure we are using the comma operator. Without the parentheses, it would look like we are passing a bunch of arguments to our array initialization, but really, we are evaluating the index, casting it to void, ignoring that void result, and then returning value, which is copied into the array.
The final argument, the one we call std::forward on, is a minor optimization. If someone passes in a temporary std::string and says "make an array of 5 of these", we would like to have 4 copies and 1 move, instead of 5 copies. The std::forward ensures that we do this.
The full code, including headers and some unit tests:
#include <array>
#include <type_traits>
#include <utility>
namespace detail {
template<std::size_t size, typename T, std::size_t... indexes>
constexpr auto make_array_n_impl(T && value, std::index_sequence<indexes...>) {
// Use the comma operator to expand the variadic pack
// Move the last element in if possible. Order of evaluation is well-defined
// for aggregate initialization, so there is no risk of copy-after-move
return std::array<std::decay_t<T>, size>{ (static_cast<void>(indexes), value)..., std::forward<T>(value) };
}
} // namespace detail
template<typename T>
constexpr auto make_array_n(std::integral_constant<std::size_t, 0>, T &&) {
return std::array<std::decay_t<T>, 0>{};
}
template<std::size_t size, typename T>
constexpr auto make_array_n(std::integral_constant<std::size_t, size>, T && value) {
return detail::make_array_n_impl<size>(std::forward<T>(value), std::make_index_sequence<size - 1>{});
}
template<std::size_t size, typename T>
constexpr auto make_array_n(T && value) {
return make_array_n(std::integral_constant<std::size_t, size>{}, std::forward<T>(value));
}
struct non_copyable {
constexpr non_copyable() = default;
constexpr non_copyable(non_copyable const &) = delete;
constexpr non_copyable(non_copyable &&) = default;
};
int main() {
constexpr auto array_n = make_array_n<6>(5);
static_assert(std::is_same<std::decay_t<decltype(array_n)>::value_type, int>::value, "Incorrect type from make_array_n.");
static_assert(array_n.size() == 6, "Incorrect size from make_array_n.");
static_assert(array_n[3] == 5, "Incorrect values from make_array_n.");
constexpr auto array_non_copyable = make_array_n<1>(non_copyable{});
static_assert(array_non_copyable.size() == 1, "Incorrect array size of 1 for move-only types.");
constexpr auto array_empty = make_array_n<0>(2);
static_assert(array_empty.empty(), "Incorrect array size for empty array.");
constexpr auto array_non_copyable_empty = make_array_n<0>(non_copyable{});
static_assert(array_non_copyable_empty.empty(), "Incorrect array size for empty array of move-only.");
}
The page you linked states
If an explicit array size is specified, but an shorter initiliazation list is specified, the unspecified elements are set to zero.
Speed issue: Any differences would be negligible for arrays this small. If you work with large arrays and speed is much more important than size, you can have a const array of the default values (initialized at compile time) and then memcpy them to the modifiable array.
Another way of initializing the array to a common value, would be to actually generate the list of elements in a series of defines:
#define DUP1( X ) ( X )
#define DUP2( X ) DUP1( X ), ( X )
#define DUP3( X ) DUP2( X ), ( X )
#define DUP4( X ) DUP3( X ), ( X )
#define DUP5( X ) DUP4( X ), ( X )
.
.
#define DUP100( X ) DUP99( X ), ( X )
#define DUPx( X, N ) DUP##N( X )
#define DUP( X, N ) DUPx( X, N )
Initializing an array to a common value can easily be done:
#define LIST_MAX 6
static unsigned char List[ LIST_MAX ]= { DUP( 123, LIST_MAX ) };
Note: DUPx introduced to enable macro substitution in parameters to DUP
For the case of an array of single-byte elements, you can use memset to set all elements to the same value.
There's an example here.
The simplest way is to use std::array and write a function template that will return the required std::array with all of its element initialized with the passed argument as shown below.
C++11 Version
template<std::size_t N> std::array<int, N> make_array(int val)
{
std::array<int, N> tempArray{};
for(int &elem:tempArray)
{
elem = val;
}
return tempArray;
}
int main()
{
//---------------------V-------->number of elements
auto arr = make_array<8>(5);
//------------------------^---->value of element to be initialized with
//lets confirm if all objects have the expected value
for(const auto &elem: arr)
{
std::cout << elem << std::endl; //prints all 5
}
}
Working demo
C++17 Version
With C++17 you can add constexpr to the function template so that it can be used in constexpr context:
//-----------------------------------------vvvvvvvvv--->added constexpr
template<std::size_t N> std::array<int, N> constexpr make_array(int val)
{
std::array<int, N> tempArray{};
for(int &elem:tempArray)
{
elem = val;
}
return tempArray;
}
int main()
{
//--vvvvvvvvv------------------------------>constexpr added
constexpr auto arr = make_array<8>(5);
for(const auto &elem: arr)
{
std::cout << elem << std::endl;
}
}
Working demo
1) When you use an initializer, for a struct or an array like that, the unspecified values are essentially default constructed. In the case of a primitive type like ints, that means they will be zeroed. Note that this applies recursively: you could have an array of structs containing arrays and if you specify just the first field of the first struct, then all the rest will be initialized with zeros and default constructors.
2) The compiler will probably generate initializer code that is at least as good as you could do by hand. I tend to prefer to let the compiler do the initialization for me, when possible.
In the C++ programming language V4, Stroustrup recommends using vectors or valarrays over builtin arrays. With valarrary's, when you create them, you can init them to a specific value like:
valarray <int>seven7s=(7777777,7);
To initialize an array 7 members long with "7777777".
This is a C++ way of implementing the answer using a C++ data structure instead of a "plain old C" array.
I switched to using the valarray as an attempt in my code to try to use C++'isms v. C'isms....