Нет проверки границ массива при использовании авто - PullRequest
10 голосов
/ 03 июня 2019

При наличии этот код компилируется с -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];
}

Ответы [ 4 ]

7 голосов
/ 03 июня 2019

хотя это должен быть тот же тип

Вы бы так подумали. Но если вы проверите, вы обнаружите, что они на самом деле не имеют тот же тип:

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&&.

4 голосов
/ 03 июня 2019

Но не в строке 16, даже если он должен быть того же типа

Предполагая, что это вы ссылаетесь на auto array1 = AllFormats(), тогда это не такимеют тот же тип.auto никогда не выводится как ссылка, поэтому array1 не является ссылкой.Это не ссылка, и выводится как потерянный результат, то есть указатель на Format.

Поскольку тип указателя не несет информацию о размере указанного массива, компилятор нене может доказать, что оператор индексирования переполняет массив.

Чтобы объявить ссылку, вы можете использовать:

auto&          array1 = AllFormats(); // 1.
auto&&         array1 = AllFormats(); // 2.
decltype(auto) array1 = AllFormats(); // 3.
  1. Явно объявляет ссылку lvalue.
  2. Объявляет универсальную ссылку, которая сворачивается в ссылку lvalue, потому что AllFormats возвращает ссылку lvalue.Ссылка на rvalue была бы справочной, если AllFormats вернул Format&&.
  3. auto тип вычета использует правила, отличные от decltype вычета.Одно ключевое отличие состоит в том, что auto никогда не является ссылкой, в то время как decltype(E); может быть ссылкой, в зависимости от выражения E.decltype(auto) var = E разрешает объявление с использованием правил decltype, как если бы использовалось decltype(E).
4 голосов
/ 03 июня 2019

array1 - указатель.

Используйте auto&& вместо auto там.

2 голосов
/ 03 июня 2019

In

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

array1 не является массивом, поэтому границы не могут быть проверены.Даже если вы вернетесь по ссылке, auto не будет выводить единицу, поэтому вместо этого массив распадается на указатель, а array1 получает вывод как Format *.

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

Создает предупреждение, потому что array2 является ссылкой на массив, поэтому он знает размер.


Чтобы получить auto для вывода массива, вам нужно либо использовать auto&, который будет работать только в том случае, если возвращаемое значение являетсяссылка lvalue, или auto&&, которая будет связывать ссылку с чем-либо.

...