Treat C Cstyle Array as Std::Array

Treat C cstyle array as std::array

You cannot do that. The std::array is an aggregate and holds its own block of data (as opposed to a pointer to a block of data that can be easily reassigned). So there's no way of avoiding a copy of all the elements. In C++11 this is particularly important because the array's data cannot be moved, so there's no efficient std::swap function, for example.

std::array vs C-Style array for contiguous memory

For the reference, the technique you are referring to is called flexible array member, and unfortunately, it is not supported in C++ as either core feature or standard library function. I find this disappointing.

std::array is a dressed-up C-style array (with some members which allow it's using as an STL container, like iteration, sizing, type introspection, etc).

The only way I know to achieve a functionality which would be similar to flexible array member would be to create an std::vector<char>, with size set to the sizeof(header) + <extra bytes for payload>, and than placement new header in the vector.data(). You can wrap all this in a helper class to save yourself some typing for multiple scenarios.

Is a C-Style array to std::array transition completely safe for arrays?

For one-dimensional arrays, this might work in all cases, the 2D case is more tricky:

In principle, it is possible for the std::array < > template to only consist of the array itself because its length argument is a compile time variable which does not need to be stored. However, your STL-implementation might have chosen to store it anyway, or any other data it needs. So, while '&a[n] == &a[0] + n' holds for any std::array, the expression '&a[n][0] == &a[0][0] + n*arrayWidth' might not hold for a 'std::array < std::array, arrayHeight >'.

Still you might want to check whether 'sizeof(std::array < int, 100 >) == sizeof(int) * 100' with your STL-implementation. If it does, it should be safe to replace even the 2D arrays.

How to initialize std::vector from C-style array?

Don't forget that you can treat pointers as iterators:

w_.assign(w, w + len);

Sort 2 dimensional c array with std::sort

Maybe there is some way to turn it into a std::array without copying
it?

Perhaps not turning into a std::array per se, but an alternative approach might be to cast the 2D C-style arrays into a std::array reference just for the sorting. Doing so in reliance on the standard saying an std::array representation in memory at least begins with its C-style array equivalent. See here under [array.overview§2]:

An array is an aggregate that can be list-initialized with up to N
elements whose types are convertible to T.

In practice, the following usage of reinterpret_cast is most probably safe, but do note that unless there is a special exception for it somewhere in the standard, it would formally be undefined behaviour:

#include <algorithm>
#include <array>
#include <iostream>

int main() {
auto two_dim_less = [](std::array<int, 2>& a, std::array<int, 2>& b) {
return a[1] < b[1]; };

int two_dim[][2] = {{1, 8}, {2, 4}, {3, 10}, {4, 40}, {5, 1}};

std::array<std::array<int, 2>, 5>& arr =
*reinterpret_cast<std::array<std::array<int, 2>, 5>*>(&two_dim);

std::sort(arr.begin(), arr.end(), two_dim_less);

for (int i = 0; i < 5; i++)
std::cout << two_dim[i][0] << ", " << two_dim[i][1] << '\n';

return 0;
}

Output:

5, 1
2, 4
1, 8
3, 10
4, 40

Regarding the use of std::qsort(), note that it is potentially slower than std::sort() due to the latter allowing to inline the comparisons while the former doesn't.

c-style array better container than std::array and std::vector for fixed size data that benifits from move semantics?

I decided to follow juanchopanza's advice and go for the std::vector. As I already mentioned in the question it can be moved (in O(1)) and as juanchopanza pointed out, the overhead is very small.

Furthermore I like that it gives me the option to declare the move operations as default, increasing code readability. (I wanted to add a virtual destructor so I needed to add them as the compiler won't generate them when a destructor is specified)

For those interested, the relevant declarations:

/// Copy constructor
VectorND(const VectorND &) = default;
/// Move constructor
VectorND(VectorND &&) = default;
/// Copy assignment
VectorND& operator=(const VectorND &) = default;
/// Move assignment
VectorND& operator=(VectorND &&) = default;
/// Destructor
virtual ~VectorND() = default;

Replace fixed size arrays with std::array?

AFAIK std::array just provides a nicer STL-like interface to deal with rather than normal C-arrays. In terms of performances and capabilities the two choices boil down to be pretty much the same but std::array can be used as a standard container.

Another feature: they might be treated like tuples since they provide tuple-like access functions.

Last but not the least as user2079303 noticed: if your code is going to be used by novice programmers, it can prevent the array decaying process when passing it as an argument.

If you're wondering if you should just replace all your C-style arrays with std::arrays, the answer is yes if you're going to exploit some of the newly available features and/or rewrite some of your code to take advantage of those capabilities. If you're just plain substituting C-arrays with std::arrays (without touching anything else) it might not be worth it.

Getting reference to the raw array from std::array

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.



Related Topics



Leave a reply



Submit