使用自动时不检查数组边界

No array bounds check when using auto

this code-Warray-bounds 编译时。我在声明 array2 array index 3 is past the end of the array (which contains 3 elements) 时收到警告。但不是在声明 array1 时,即使它必须是相同的类型,因此携带相同的大小信息。这是 clang 中的错误吗?

enum class Format : int {
  Off = 55,
  FormatA = 66,
  FormatB = 77,
};

inline Format (&AllFormats())[3] {
  static Format values[] = {
    Format::Off,
    Format::FormatA,
    Format::FormatB
  };
  return values;
}

int main()
{
    auto array1 = AllFormats();    
    auto v3 = array1[3];

    Format (&array2)[3] = AllFormats();    
    v3 = array2[3];
}

array1是一个指针。

在那里使用auto&&而不是auto

even though it has to be the same type

你会这么想的。但是如果你检查一下,你会发现它们实际上不是同一个类型:

std::cout << typeid(array1).name() << "\n";
std::cout << typeid(array2).name() << "\n";
P6Format
A3_6Format

糟糕。 AllFormats 返回的数组在分配给 auto 变量时衰减为指针,因为这就是 auto 的类型推导规则的工作方式。比较:

int& foo() {
    static int x = 42;
    return x;
}

auto x = foo(); // Type of `x` is `int`, not `int&`.

为防止出现这种情况,请将 array1 声明为 auto&auto&&

auto array1 = AllFormats();    
auto v3 = array1[3];

array1 不是数组,因此无法检查边界。即使您通过引用 return,auto 也不会推导一个,因此数组会衰减为指针,并且 array1 会推导为 Format *.

Format (&array2)[3] = AllFormats();    
v3 = array2[3];

生成警告,因为 array2 是对数组的引用,所以它知道大小。


要让 auto 推导一个数组,您需要使用 auto&,只有当 returned 是左值引用时才有效,或者 auto&& 这将绑定对任何内容的引用。

But not at line 16 even though it has to be the same type

假设 你指的是 auto array1 = AllFormats(),那么它没有相同的类型。 auto 永远不会被推断为引用,因此 array1 不是引用。它是一个 non-reference,并被推断为衰减结果,即指向 Format.

的指针

由于指针类型不携带有关指向数组大小的信息,编译器无法证明下标运算符溢出数组。

要声明引用,您可以使用:

auto&          array1 = AllFormats(); // 1.
auto&&         array1 = AllFormats(); // 2.
decltype(auto) array1 = AllFormats(); // 3.
  1. 显式声明一个左值引用。
  2. 声明一个通用引用,它折叠成一个左值引用,因为 AllFormats returns 一个左值引用。如果 AllFormats 返回 Format&&.
  3. ,它将是一个右值引用
  4. auto 类型推导使用与 decltype 推导不同的规则。一个关键区别是 auto 永远不是引用,而 decltype(E); 可能是引用,具体取决于表达式 Edecltype(auto) var = E 允许使用 decltype 规则进行声明,就像使用 decltype(E) 一样。