2016-05-08 4 views
3

Я обнаружил, что когда простая структура данных с конструкторами по умолчанию содержит массив, конструктор по умолчанию может вызываться с другим числом аргументов, то есть:Длина строки инициализатора C++ 11 не проверяется в неявном конструкторе

struct LayerData 
{ 
    uint32_t d[60]; 
}; 

Может быть инициализирован:

LayerData in({rand(),rand(),rand(),rand(),rand()}); 

И он компилирует правильно.

Является ли это ожидаемым поведением в C++ 11? Нет ли проверки целостности во время компиляции в неявном конструкторе?

+0

gcc version 4.8.4 (Ubuntu 4.8.4-2ubuntu1 ~ 14.04.1) – AndresR

+0

8.5.1/4: «Агрегат, который является классом, также может быть инициализирован одним выражением, не заключенным в фигурные скобки». Дело не в том, что длина не проверяется; скорее, ваша инициализация похожа на 'int a [3] = {1};'. –

+0

Хорошо, я просто ожидал 'int a [3] = {1};' также терпеть неудачу. Но в нижеприведенном ответе он указан в стандарте, которого нет. – AndresR

ответ

3

N3337 8.5.1/7

Если количество инициализатора-пункты в списке, чем входящие в совокупности, то каждый член явно не инициализированы должен быть инициализирован с пустым список инициализаторов (8.5.4).

struct S { int a; const char* b; int c; }; 
S ss = { 1, "asdf" }; 

инициализирует ss.a с 1, ss.b с "ASDF", и ss.c со значением выражения вида Int(), то есть 0 ,

Таким образом, в вашем примере первые 5 элементов инициализируются rand() другой с int(), который 0.

+0

Спасибо за ссылку – AndresR

+0

Это, похоже, не объясняет, почему оно работает с круглыми скобками, а не в фигурных скобках. 'T var (...)' всегда должен вызывать конструктор. И 'T' в этом случае является агрегатом, который не имеет конструктора. Так что это не должно работать вообще. –

+0

@NicolBolas Я думал, что OP не спрашивал об этом. Во всяком случае, это интересный вопрос. – PcAF

2

Существует проверка времени компиляции. Это не компилируется:

struct A 
{ 
    int b[3]; 
}; 

int main() 
{ 
    A a { 1, 2 };  // no problem here. equivalent to 1, 2, 0 
    A b { 1, 2, 3, 4 }; // problem here. too many initializers 
} 

Потому что:

/tmp/gcc-explorer-compiler11648-73-eh15v1/example.cpp: In function 'int main()': 
10 : error: too many initializers for 'A' 
A b { 1, 2, 3, 4 }; 
^ 
Compilation failed 

Массив может быть инициализирован с меньшим количеством деталей, чем она содержит. В этом случае оставшиеся элементы инициализируются значением (т. Е. Нулем).

0

GCC 4.8.4 скомпилирует код в порядке. Не каждый компилятор, MSVC++ 14 (VS 2015) не компилируется

LayerData in({rand(),rand(),rand(),rand(),rand()}); 

Но это скомпилировать

LayerData in{{rand(),rand(),rand(),rand(),rand()}}; 

Использование C++ 11 Универсальные обозначения

Глядя на причины разрешений GCC это , похоже, потому, что он выделяет тип в стеке, а затем использует XMM для перемещения 32 бит (64 при компиляции по x64) за раз в ячейку памяти. Возможно, это изменяется с размером типа, но обычно я бы сказал, что если вам нужно обеспечить такую ​​же длину, вы не должны подвергать такой тип, как это в любом случае.Обратите внимание, что универсальные обозначения на самом деле могут быть использованы в ваших интересах и с помощью шаблонов вы можете сформировать то, что выглядит очень похожее на то, что вы пытаетесь сделать, и претворяет такое же количество аргументов:

#include <cstdint> 

template <int N> 
class LayerData 
{ 
    static_assert(N > 0, "Number must be greater than 0"); 
public: 
    uint32_t d[N]; 
    template<typename... Args> 
    LayerData(Args&&... args) : d{uint32_t(args)...} 
    { 
     static_assert(sizeof...(Args) == N, "Invalid number of constructor arguments."); 
    } 
}; 

int main() 
{ 
    LayerData<4> in1{1, 2, 3, 4}; //Fine 
    LayerData<60> in2{1, 2, 3, 4}; //Causes error "Invalid number of constructor arguments." 
} 

наспех, но вы должны получить эту идею.