【问题标题】:No array bounds check when using auto使用自动时没有数组边界检查
【发布时间】:2019-10-19 02:14:33
【问题描述】:

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];
}

【问题讨论】:

  • 您如何验证它们具有相同的类型?您可能需要重新检查。
  • 使用 std::array<Format, 3> 代替 C 样式的数组。你会发现代码更容易写,也更容易理解。
  • @pete 你是对的。在我的代码中,我会使用 std::array。显示的代码大致由 google flatbuffers 编译器生成。

标签: c++ clang clang++


【解决方案1】:

即使它必须是相同的类型

你会这么想的。但是如果你检查,你会发现它们实际上没有相同的类型:

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&amp;auto&amp;&amp;

【讨论】:

  • 就是这样的东西真的让我对C++失去了信心!
  • @LightnessRacesinOrbit 推论规则不直观,但至少它们是一致的。通过简单地从不使用 C 数组,可以避免很多这些问题。就我个人而言,我违反了这条规则,但我可能应该吞下苦药并在任何地方使用std::array。不过,这些都不应该减损您的有效批评。
  • 确实如此。我已经开始更喜欢std::array,但我不能保证我总是打扰;)
【解决方案2】:

array1 是一个指针。

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

【讨论】:

  • 太好了,修复了它。但我需要更多地了解auto&amp;&amp; 的实际含义。谢谢
  • decltype(auto) 也可以像这里指出的那样工作:stackoverflow.com/questions/16949016/…
  • @ValeriySavchenko 当然,但这更冗长,并且在实践中(例如 9999/ 10000 次)。
【解决方案3】:

但不是在第 16 行,即使它必须是相同的类型

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

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

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

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

【讨论】:

    【解决方案4】:

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

    array1 不是数组,因此无法检查边界。即使您通过引用返回,auto 也不会推断出一个,因此数组会衰减为一个指针,array1 会推断为Format *

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

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


    要让auto 推断出一个数组,您要么需要使用auto&amp;,这仅在返回的是左值引用时才有效,要么是auto&amp;&amp;,它将绑定到任何东西的引用。

    【讨论】:

      猜你喜欢
      • 2018-04-17
      • 1970-01-01
      • 1970-01-01
      • 2013-09-23
      • 1970-01-01
      • 1970-01-01
      • 2023-04-03
      • 1970-01-01
      • 2014-03-09
      相关资源
      最近更新 更多