为什么静态大小的数组类型不能是容器类型?

Pap*_*ter 3 c++ containers stl

我有一个静态大小的数组的别名,使用它很容易和直接:

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;
Run Code Online (Sandbox Code Playgroud)

我甚至可以在容器中使用别名:

std::vector<triplet_t> vt;
Run Code Online (Sandbox Code Playgroud)

或者我以前认为,因为一旦你使用vt它失败:

vt.push_back({});
Run Code Online (Sandbox Code Playgroud)

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(); }
                    ~~~~~~^~~
Run Code Online (Sandbox Code Playgroud)

问题似乎是在展开所有模板技巧后,一个placement-new被称为转发所有提供括号的参数,显然这不是初始化静态大小数组的方法.

此外,某种程度上容器认为triplet_t是一个对象,因此要求析构函数,再次失败编译.没有别名,问题显然是一样的:

std::vector<std::uint8_t[3]> vt;
vt.push_back({});          // Boom!
vt.push_back({255, 0, 0}); // Ouch!
Run Code Online (Sandbox Code Playgroud)

但是使用struct具有相同内存布局的问题没有问题:

struct rgb { std::uint8_t r, g, b; };
std::vector<rgb> vt;
vt.push_back({});          // Nice!
vt.push_back({255, 0, 0}); // Cool!
Run Code Online (Sandbox Code Playgroud)

我想知道为什么会发生这种情况,有没有办法在容器中使用静态大小的数组作为包含类型?

YSC*_*YSC 5

阅读std::vector文档,您可以发现T必须满足CopyAssignable和CopyConstructible 的要求.

这意味着(简化):with vt两个类型实例T,表达式t = v必须合法.显然,如果T是本机数组,则不是这种情况(您不能将C数组分配给另一个),并且某些函数std::vector<T>将是错误的.

解决方案是定义triplet_t为:

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({});
Run Code Online (Sandbox Code Playgroud)