Related
What's the canonical way to get the reference to std::array's underlying raw (C) array?
The data() method returns just a raw pointer, which makes it unsuitable e.g. for passing into functions which accept a reference to a raw array of a known size.
Also, is there a good reason why data() returns a raw pointer, and not a reference to the underlying raw array, or is this just an oversight?
What's the canonical way to get an std::array's underlying raw (C)
array?
There is no way of getting the underlying C array.
Also, is there a good reason why data() returns a raw pointer, and not
a reference to the underlying raw array, or is this just an oversight?
It's backwards: there is no good reason for the std::array to provide the underlying C array. As you already said, the C array would be useful (over the raw pointer) only with functions getting a reference to C arrays.
When was the last time you had a function:
void foo(int (&arr)[5])
Me? Never. I never saw a function with a C array reference parameter with the exception of getting the size of array (and rejecting pointers):
template <class T, std::size_t N>
auto safe_array_size(T (&)[N]) { return N; }
Let's dive a little into why parameters references to arrays are not used.
For starters, from the C area pointer with a separate size parameter was the only way to pass arrays around, due to array-to-pointer decay and lack of reference type.
In C++ there are alternatives to C arrays, like std::vector and std::array. But even when you have a (legacy) C array you have 2 situations:
if you pass it to a C function you don't have the option of reference, so you are stuck to pointer + size
when you want to pass it to a C++ function the idiomatic C++ way is to pass begin + end pointers.
First of all a begin + end iterators is generic, it accepts any kind of containers. But is not uncommon to see reference to std::vector when you want to avoid templates, so why not reference to C array if you have one? Because of a big drawback: you have to know the size of the array:
void foo(int (&arr)[5])
which is extremely limiting.
To get around this you need to make it a template:
template <std::size N>
void foo(int (&arr)[N])
which beats the purpose of avoiding templates, so you better go with begin + end template iterators instead.
In some cases (e.g. math calculations on just 2 or 3 values which have
the same semantics, so they shouldn't be separate parameters) a
specific array size is called for, and making the function generic
wouldn't make sense. In those cases, specifying the size of the array
guarantees safety since it only allows passing in an array of the
correct size at compile-time; therefore it's advantageous and isn't a
"big drawback"
One of the beauties of (C and) C++ is the enormous scope of applicability. So yes, you will always find some fields that use or need a certain unique feature in an unique way. That being said, even in your example I would still shy away from arrays. When you have a fixed number of values that shouldn't be semantically separated I think a structure would be the correct choice over arrays most of the time (e.g. glm::mat4 instead of float[4]).
But let's not forget what std::array is: a modern replacement for C arrays. One thing I learned when analyzing options is that there is no absolute "better than". There is always a "depends". But not in this case: std::array should unquestionably replace C arrays in interfaces. So in the rare case where a fixed size container is needed as a reference parameter it doesn't make sense to enable encouraging the use of C arrays when you already have an std::array. So the only valid case where exposing the underlying C array of std::array is need is for some old libraries that have C array reference parameters. But I think that in the bigger picture adding this to the interface it is not justified. New code should use a struct (btw std::tuple is getting easier and easier to use by each standard) or std::array.
AFAIK, There's no direct or typesafe way to do it, but one work around if you need to pass to a function (with a signature you cannot change to std::array) is by usingreinterpret_cast like this:
some_function(*reinterpret_cast<int (*)[myarr.size()]>(myarr.data())));
If you wanted to make it safer:
#include <array>
void passarray(int (&myarr)[5]){}
template <typename ValueT, std::size_t size>
using CArray = ValueT[size];
template <typename ValueT, std::size_t size>
CArray<ValueT, size> & c_array_cast(std::array<ValueT, size> & arg) {
{
return *reinterpret_cast<CArray<ValueT,size>*>(arg.data());
}
int main()
{
std::array<int,5> myarr = { {1,2,3,4,5} };
passarray(*reinterpret_cast<int (*)[myarr.size()]>(myarr.data()));
passarray(c_array_cast(myarr));
return 0;
}
There isn't one.
I can see why it would be useful, especially when working with legacy code, but since a couple of decades ago we're supposed to be moving away from code like that and towards iterator-aware algorithms. And when working with C code you'd have to use a pointer anyway. I presume these are factors in the decision not to provide this functionality.
Rewrite your code to accept std::array<T, N>& instead, if possible.
You can reinterpret_cast the .data() to a raw, like:
template <typename T, std::size_t N>
inline static decltype(auto) to_raw_array(const std::array<T, N> & arr_v) {
return reinterpret_cast<const T(&) [N]>(*arr_v.data());
}
But it is an ugly hack. As the others have already suggested, I recommend you to use std::array as-is.
Usage:
#include <cstdint>
#include <array>
template <typename T, std::size_t N>
inline static decltype(auto) to_raw_array(const std::array<T, N> & arr_v) {
return reinterpret_cast<const T(&) [N]>(*arr_v.data());
}
void foo(const std::uint8_t(&buf)[5]){
// ...
}
int main(void){
std::array<std::uint8_t, 5> arr = {1,2,3,4,5};
foo(to_raw_array(arr));
}
Why not passing std::array.begin()? Worked in SDL2 on:
int SDL_RenderDrawLines(SDL_Renderer *renderer, const SDL_Point *points, int count)
My line to be drawed:
std::array<SDL_Point, 8> a_line;
I passed like this:
SDL_RenderDrawLines(r_s_game.p_renderer, a_line.begin(), 8);
Pardon my ignorance, it appears to me that std::array is meant to be an STL replacement for your regular arrays. But because the array size has to be passed as a template parameter, it prevents us from creating std::array with a size known only at runtime.
std::array<char,3> nums {1,2,3}; // Works.
constexpr size_t size = 3;
std::array<char,size> nums {1,2,3}; // Works.
const buf_size = GetSize();
std::array<char, buf_size> nums; // Doesn't work.
I would assume that one very important use case for an array in C++ is to create a fixed size data structure based on runtime inputs (say allocating buffer for reading files).
The workarounds I use for this are:
// Create a array pointer for on-the-spot usecases like reading from a file.
char *data = new char[size];
...
delete[] data;
or:
// Use unique_ptr as a class member and I don't want to manage the memory myself.
std::unique_ptr<char[]> myarr_ = std::unique_ptr<char[]>(new char[size]);
If I don't care about fixed size, I am aware that I can use std::vector<char> with the size pre-defined as follows:
std::vector<char> my_buf (buf_size);
Why did the designers of std::array choose to ignore this use case? Perhaps I don't understand the real usecase for std::array.
EDIT: I guess another way to phrase my question could also be - Why did the designers choose to have the size passed as a template param and not as a constructor param? Would opting for the latter have made it difficult to provide the functionality that std::array currently has? To me it seems like a deliberate design choice and I don't understand why.
Ease of programming
std::array facilitates several beneficial interfaces and idioms which are used in std::vector. With normal C-style arrays, one cannot have .size() (no sizeof hack), .at() (exception for out of range), front()/back(), iterators, so on. Everything has to be hand-coded.
Many programmers may choose std::vector even for compile time known sized arrays, just because they want to utilize above programming methodologies. But that snatches away the performance available with compile time fixed size arrays.
Hence std::array was provided by the library makers to discourage the C-style arrays, and yet avoid std::vectors when the size is known at the compile time.
The two main reasons I understand are:
std::array implements STL's interfaces for collection-types, allowing an std::array to be passed as-is to functions and methods that accept any STL iterator.
To prevent array pointer decay... (below)
...this is the preservation of type information across function/method boundaries because it prevents Array Pointer Decay.
Given a naked C/C++ array, you can pass it to another function as a parameter argument by 4 ways:
void by_value1 ( const T* array )
void by_value2 ( const T array[] )
void by_pointer ( const T (*array)[U] )
void by_reference( const T (&array)[U] )
by_value1 and by_value2 are both semantically identical and cause pointer decay because the receiving function does not know the sizeof the array.
by_pointer and by_reference both requires that U by a known compile-time constant, but preserve sizeof information.
So if you avoid array decay by using by_pointer or by_reference you now have a maintenance problem every time you change the size of the array you have to manually update all of the call-sites that have that size in U.
By using std::array it's taken care of for you by making those functions template functions where U is a parameter (granted, you could still use the by_pointer and by_reference techniques but with messier syntax).
...so std::array adds a 5th way:
template<typename T, size_t N>
void by_stdarray( const std::array<T,N>& array )
std::array is a replacement for C-style arrays.
The C++ standards don't allow C-style arrays to be declared without compile-time defined sizes.
What's the canonical way to get the reference to std::array's underlying raw (C) array?
The data() method returns just a raw pointer, which makes it unsuitable e.g. for passing into functions which accept a reference to a raw array of a known size.
Also, is there a good reason why data() returns a raw pointer, and not a reference to the underlying raw array, or is this just an oversight?
What's the canonical way to get an std::array's underlying raw (C)
array?
There is no way of getting the underlying C array.
Also, is there a good reason why data() returns a raw pointer, and not
a reference to the underlying raw array, or is this just an oversight?
It's backwards: there is no good reason for the std::array to provide the underlying C array. As you already said, the C array would be useful (over the raw pointer) only with functions getting a reference to C arrays.
When was the last time you had a function:
void foo(int (&arr)[5])
Me? Never. I never saw a function with a C array reference parameter with the exception of getting the size of array (and rejecting pointers):
template <class T, std::size_t N>
auto safe_array_size(T (&)[N]) { return N; }
Let's dive a little into why parameters references to arrays are not used.
For starters, from the C area pointer with a separate size parameter was the only way to pass arrays around, due to array-to-pointer decay and lack of reference type.
In C++ there are alternatives to C arrays, like std::vector and std::array. But even when you have a (legacy) C array you have 2 situations:
if you pass it to a C function you don't have the option of reference, so you are stuck to pointer + size
when you want to pass it to a C++ function the idiomatic C++ way is to pass begin + end pointers.
First of all a begin + end iterators is generic, it accepts any kind of containers. But is not uncommon to see reference to std::vector when you want to avoid templates, so why not reference to C array if you have one? Because of a big drawback: you have to know the size of the array:
void foo(int (&arr)[5])
which is extremely limiting.
To get around this you need to make it a template:
template <std::size N>
void foo(int (&arr)[N])
which beats the purpose of avoiding templates, so you better go with begin + end template iterators instead.
In some cases (e.g. math calculations on just 2 or 3 values which have
the same semantics, so they shouldn't be separate parameters) a
specific array size is called for, and making the function generic
wouldn't make sense. In those cases, specifying the size of the array
guarantees safety since it only allows passing in an array of the
correct size at compile-time; therefore it's advantageous and isn't a
"big drawback"
One of the beauties of (C and) C++ is the enormous scope of applicability. So yes, you will always find some fields that use or need a certain unique feature in an unique way. That being said, even in your example I would still shy away from arrays. When you have a fixed number of values that shouldn't be semantically separated I think a structure would be the correct choice over arrays most of the time (e.g. glm::mat4 instead of float[4]).
But let's not forget what std::array is: a modern replacement for C arrays. One thing I learned when analyzing options is that there is no absolute "better than". There is always a "depends". But not in this case: std::array should unquestionably replace C arrays in interfaces. So in the rare case where a fixed size container is needed as a reference parameter it doesn't make sense to enable encouraging the use of C arrays when you already have an std::array. So the only valid case where exposing the underlying C array of std::array is need is for some old libraries that have C array reference parameters. But I think that in the bigger picture adding this to the interface it is not justified. New code should use a struct (btw std::tuple is getting easier and easier to use by each standard) or std::array.
AFAIK, There's no direct or typesafe way to do it, but one work around if you need to pass to a function (with a signature you cannot change to std::array) is by usingreinterpret_cast like this:
some_function(*reinterpret_cast<int (*)[myarr.size()]>(myarr.data())));
If you wanted to make it safer:
#include <array>
void passarray(int (&myarr)[5]){}
template <typename ValueT, std::size_t size>
using CArray = ValueT[size];
template <typename ValueT, std::size_t size>
CArray<ValueT, size> & c_array_cast(std::array<ValueT, size> & arg) {
{
return *reinterpret_cast<CArray<ValueT,size>*>(arg.data());
}
int main()
{
std::array<int,5> myarr = { {1,2,3,4,5} };
passarray(*reinterpret_cast<int (*)[myarr.size()]>(myarr.data()));
passarray(c_array_cast(myarr));
return 0;
}
There isn't one.
I can see why it would be useful, especially when working with legacy code, but since a couple of decades ago we're supposed to be moving away from code like that and towards iterator-aware algorithms. And when working with C code you'd have to use a pointer anyway. I presume these are factors in the decision not to provide this functionality.
Rewrite your code to accept std::array<T, N>& instead, if possible.
You can reinterpret_cast the .data() to a raw, like:
template <typename T, std::size_t N>
inline static decltype(auto) to_raw_array(const std::array<T, N> & arr_v) {
return reinterpret_cast<const T(&) [N]>(*arr_v.data());
}
But it is an ugly hack. As the others have already suggested, I recommend you to use std::array as-is.
Usage:
#include <cstdint>
#include <array>
template <typename T, std::size_t N>
inline static decltype(auto) to_raw_array(const std::array<T, N> & arr_v) {
return reinterpret_cast<const T(&) [N]>(*arr_v.data());
}
void foo(const std::uint8_t(&buf)[5]){
// ...
}
int main(void){
std::array<std::uint8_t, 5> arr = {1,2,3,4,5};
foo(to_raw_array(arr));
}
Why not passing std::array.begin()? Worked in SDL2 on:
int SDL_RenderDrawLines(SDL_Renderer *renderer, const SDL_Point *points, int count)
My line to be drawed:
std::array<SDL_Point, 8> a_line;
I passed like this:
SDL_RenderDrawLines(r_s_game.p_renderer, a_line.begin(), 8);
I am new to C++ and currently learning it with a book by myself. This book seems to say that there are several kinds of arrays depending on how you declare it. I guess the difference between dynamic arrays and static arrays are clear to me. But I do not understand the difference between the STL std::array class and a static array.
An STL std::array variable is declared as:
std::array < int, arraySize > array1;
Whereas a static array variable is declared as:
int array1[arraySize];
Is there a fundamental difference between the two? Or is it just syntax and the two are basically the same?
A std::array<> is just a light wrapper around a C-style array, with some additional nice interface member functions (like begin, end etc) and typedefs, roughly defined as
template<typename T, size_t N>
class array
{
public:
T _arr[N];
T& operator[](size_t);
const T& operator[](size_t) const;
// other member functions and typedefs
}
One fundamental difference though is that the former can be passed by value, whereas for the latter you only pass a pointer to its first element or you can pass it by reference, but you cannot copy it into the function (except via a std::copy or manually).
A common mistake is to assume that every time you pass a C-style array to a function you lose its size due to the array decaying to a pointer. This is not always true. If you pass it by reference, you can recover its size, as there is no decay in this case:
#include <iostream>
template<typename T, size_t N>
void f(T (&arr)[N]) // the type of arr is T(&)[N], not T*
{
std::cout << "I'm an array of size " << N;
}
int main()
{
int arr[10];
f(arr); // outputs its size, there is no decay happening
}
Live on Coliru
The main difference between these two is an important one.
Besides the nice methods the STL gives you, when passing a std::array to a function, there is no decay. Meaning, when you receive the std::array in the function, it is still a std::array, but when you pass an int[] array to a function, it effectively decays to an int* pointer and the size of the array is lost.
This difference is a major one. Once you lose the array size, the code is now prone to a lot of bugs, as you have to keep track of the array size manually. sizeof() returns the size of a pointer type instead of the number of elements in the array. This forces you to manually keep track of the array size using interfaces like process(int *array, int size). This is an ok solution, but prone to errors.
See the guidelines by Bjarne Stroustroup:
https://github.com/isocpp/CppCoreGuidelines/blob/master/CppCoreGuidelines.md#Rp-run-time
That can be avoided with a better data type, which std::array is designed for, among many other STL classes.
As a side note, unless there's a strong reason to use a fixed size array, std::vector may be a better choice as a contiguous memory data structure.
std::array and C-style arrays are similar:
They both store a contiguous sequence of objects
They are both aggregate types and can therefore be initialized using aggregate initialization
Their size is known at compile time
They do not use dynamic memory allocation
An important advantage of std::array is that it can be passed by value and doesn't implicitly decay to a pointer like a C-style array does.
In both cases, the array is created on the stack.
However, the STL's std::array class template offers some advantages over the "raw" C-like array syntax of your second case:
int array1[arraySize];
For example, with std::array you have a typical STL interface, with methods like size (which you can use to query the array's element count), front, back, at, etc.
You can find more details here.
Is there a fundamental difference between the two? or is it just syntax and the two are basically the same?
There's a number of differences for a raw c-style array (built-in array) vs. the std::array.
As you can see from the reference documentation there's a number of operations available that aren't with a raw array:
E.g.: Element access
at()
front()
back()
data()
The underlying data type of the std::array is still a raw array, but garnished with "syntactic sugar" (if that should be your concern).
The key differences of std::array<> and a C-style array is that the former is a class that wraps around the latter. The class has begin() and end() methods that allow std::array objects to be easily passed in as parameters to STL algorithms that expect iterators (Note that C-style arrays can too via non member std::begin/std::end methods). The first points to the beginning of the array and the second points to one element beyond its end. You see this pattern with other STL containers, such as std::vector, std::map, std::set, etc.
What's also nice about the STL std::array is that it has a size() method that lets you get the element count. To get the element count of a C-style array, you'll have to write sizeof(cArray)/sizeof(cArray[0]), so doesn't stlArray.size() looks much more readable?
You can get full reference here:
http://en.cppreference.com/w/cpp/container/array
Usually you should prefer std::array<T, size> array1; over T array2[size];, althoug the underlying structure is identical.
The main reason for that is that std::array always knows its size. You can call its size() method to get the size. Whereas when you use a C-style array (i.e. what you called "built-in array") you always have to pass the size around to functions that work with that array. If you get that wrong somehow, you could cause buffer overflows and the function tries to read from/write to memory that does not belong to the array anymore. This cannot happen with std::array, because the size is always clear.
IMO,
Pros: It’s efficient, in that it doesn’t use any more memory than built-in fixed arrays.
Cons: std::array over a built-in fixed array is a slightly more awkward syntax, and that you have to explicitly specify the array length (the compiler won’t calculate it for you from the initializer).
In my variable data, when running "add a variable" script code, how to define a generic container for all types? And what is the generic formula to access them? It's annoying because I have to define a vector template for each type (int float double etc). My variable should contain only and only a generic vector object, whatever it's int, or float or double etc. Is it possible? Any idea?
That's the whole point of the Standard Template Library..
std::vector<int>
std::vector<float>
Two vectors - the same class, templated with different types.
If you want a container of differing type you might want to look at std::tuple
If you want a single vector that contains objects of many different types, then you might want to use boost::any, or possibly boost::variant.
Edit:
oh, you want a container that can fit in any type? Yes you can. But within certain restrictions.
See boost::any and boost::variant. boost::variant would enable you to save data of several types enumerated when declaring the boost::variant. boost::any doesn't make you enumerate all types you want to support, but you need to cast them to get back the value yourself.
In short, you must either store the type information somewhere else ( when using boost::any ), or just support a few types (say, a heterogeneous vector that supports int and double using boost::variant)
template in C++ works exactly by eliminating the need to write the same class for every type.
For example:
// function template
template <class T>
T GetMax (T a, T b) {
T result;
result = (a>b)? a : b;
return (result);
}
this GetMax should work for whatever type that has a > operator. So that is exactly what template is for in C++.
If you need more help on implementing a vector on C++ (which, by the way, is not so simple when it comes to custom types that has its own constructor and destructor. You may need allocator to get un-initialized space), read this (Implementation of Vector in C++).