Рассмотрим следующий код:
#include <iostream>
#include <vector>
#include <array>
using namespace std;
typedef double (C_array)[10];
int main()
{
std::vector<C_array> arr(10);
// let's initialize it
for (int i = 0; i < 10; i++)
for (int j = 0; j < 10; j++)
arr[i][j] = -1;
// now make sure we did the right thing
for (int i = 0; i < 10; i++)
{
for (int j = 0; j < 10; j++)
{
cout << arr[i][j] << " ";
}
cout << endl;
}
}
Я только что узнал от @juanchopanza https://stackoverflow.com/a/25108679/3093378 что этот код не должен быть законным, так как старый C
-стиль массива нельзя назначать / копировать / перемещать. тем не мение g++
пролетает через код, даже с -Wall -Wextra -pedantic
, clang++
не компилирует это. Конечно, если я попытаюсь сделать что-то вроде auto arr1 = arr;
, он терпит неудачу под g++
так как не умеет копировать arr
в arr1
,
я использовал g++4.9
от macports
под OS X Mavericks.
Живой код здесь: http://goo.gl/97koLa
Мои вопросы:
g++
так глючит? Я продолжаю находить много простых примеров, в которых g++
слепо компилирует нелегальный код, последний был вчера определяемый пользователем приоритет операторов преобразования, компилируется в g ++, но не в clang ++ , и без особых усилий, просто экспериментируя с C++
ради забавы.Ваш код недействителен C ++ 03. Во-первых, заголовок <array>
не является частью стандартной библиотеки C ++ 03, но здесь она также не нужна. Во-вторых, конструкция векторного объекта пытается вызвать конструктор
explicit vector(size_type n, const value_type& val = value_type(), const allocator_type& alloc = allocator_type());
Однако инициализация val не удалась по той же причине, по которой вы не можете написать
C_array foo = C_array();
Насколько я понимаю, параграф 2 в разделе 5.2.3 стандарта C ++ 03 допускает это обозначение только для типов, не являющихся массивами:
Выражение T (), где T — спецификатор простого типа (7.1.5.2) для типа объекта, не являющегося полным массивом, или типа void (возможно, cv-квалифицированного), создает значение rvalue указанного типа, которое является значением -инициализированный (8.5; инициализация не производится для случая void ()).
Кроме того, g ++ — 4.9.0 также отказывается компилировать код, если в командной строке не указано -std = c ++ 11:
foo.cpp: In constructor ‘std::vector<_Tp, _Alloc>::vector(std::vector<_Tp, _Alloc>::size_type, const value_type&, const allocator_type&) [with _Tp = double [10]; _Alloc = std::allocator<double [10]>; std::vector<_Tp, _Alloc>::size_type = long unsigned int; std::vector<_Tp, _Alloc>::value_type = double [10]; std::vector<_Tp, _Alloc>::allocator_type = std::allocator<double [10]>]’:
foo.cpp:11:32: error: functional cast to array type ‘std::vector<double [10]>::value_type {aka double [10]}’
std::vector<C_array> arr(10);
...
Что касается C ++ 11, то векторный контейнер предлагает дополнительный конструктор заливки:
explicit vector (size_type n);
Этот конструктор требует, чтобы тип шаблона был конструируемым по умолчанию (см. Раздел 23.3.6.2). Насколько я понимаю, это требование также не выполняется в C ++ 11 (см. Раздел 17.6.3.1), поскольку для удовлетворения требования выражение C_array () должно создать временный объект, который также недопустим в C + +11 (снова см. Раздел 5.2.3). Я не знаю, требует ли стандарт, чтобы компилятор отклонял код, или компилятору разрешено его компилировать, если не выполняется одно из требований, но реализация стандартной библиотеки просто не нуждается в нем. Может быть, люди, которые знают больше о C ++ 11, могут заполнить пробелы здесь.
Помимо всего этого, на мой взгляд, не стоит пытаться использовать массив в качестве типа элемента контейнера, поскольку другие требования контейнера не выполняются. Например, C_array не может быть вставлен при копировании, и, следовательно, вектор не может быть скопирован.
По поводу вашего второго вопроса: не стесняйтесь просматривать базу данных gcc bugzilla по адресу https://gcc.gnu.org/bugzilla/. Однако принятие недействительного кода также может быть сделано специально, например, чтобы не нарушать унаследованный код.