I have an alias to a static-sized array, using it is easy and straightforward:
using triplet_t = std::uint8_t[3];
// vvvvvvvvvvvvvvvvvv <--- easier than std::uint8_t(&triplet)[3]
void f(const triplet_t &triplet) { /* whatever */ }
triplet_t t{}; // As good as std::uint8_t t[3]{};
t[0] = '0';
t[1] = '1';
t[2] = '2';
for (auto &v : t) std::cout << v << ' ';
std::cout << '\n';
// So far so good...
triplet_t t3[3]{};
for (auto &r : t3)
for(auto &v : r)
v = 42;
I can even use the alias in containers:
std::vector<triplet_t> vt;
Or so I used to think, because as soon as you use vt it fails:
vt.push_back({});
GCC 8.0.0 201711
error: parenthesized initializer in array new [-fpermissive] { ::new((void *)__p) _Up(std::forward<_Args>(__args)...); } ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ error: request for member '~unsigned char [3]' in '* __p', which is of non-class type 'unsigned char [3]' destroy(_Up* __p) { __p->~_Up(); } ~~~~~~^~~
The issue seems to be that after unrolling all the template trickery a placement-new is called forwarding all the parameters provided parenthesized, and obviously this is not the way to initialize a static-sized array.
Also, somehow the container thinks of triplet_t as an object and hence is asking for a destructor, failing again to compile. The issue is obviously the same without the alias:
std::vector<std::uint8_t[3]> vt;
vt.push_back({}); // Boom!
vt.push_back({255, 0, 0}); // Ouch!
But no problem using a struct with the same memory layout:
struct rgb { std::uint8_t r, g, b; };
std::vector<rgb> vt;
vt.push_back({}); // Nice!
vt.push_back({255, 0, 0}); // Cool!
I wonder why this happens, is there a way to use static-sized arrays as contained type in containers?
Reading std::vector documentaion, you can find that T must meet the requirements of CopyAssignable and CopyConstructible.
This means (simplifying): with v and t two instances of type T, the expression t = v must be legal. Clearly, if T is a native array, this is not the case (you cannot assign a C-array to another), and certain functions of std::vector<T> would be ill-formed.
A solution would be to define triplet_t as:
using triplet_t = std::array<std::uint8_t, 3>;
void f(const triplet_t &triplet) { /* whatever */ }
triplet_t t{};
t[0] = '0';
t[1] = '1';
t[2] = '2';
for (auto &v : t) std::cout << v << ' ';
std::cout << '\n';
// So far so good...
triplet_t t3[3]{};
for (auto &r : t3)
for(auto &v : r)
v = 42;
std::vector<triplet_t> vt;
vt.push_back({});
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With