Рассмотрим код:
struct Foo
{
const char str[] = "test";
};
int main()
{
Foo foo;
}
Он не компилируется как с g ++, так и clang ++, выпуская по существу
error: array bound cannot be deduced from an in-class initializer
Я понимаю, что именно об этом, вероятно, говорит стандарт, но есть ли какая-то конкретная причина, почему? Поскольку у нас есть строковый литерал, кажется, что компилятор должен иметь возможность без проблем определять размер, аналогично случаю, когда вы просто объявляете вне класса const
C-подобная строка с нулевым символом в конце.
Причина в том, что у вас всегда есть возможность переопределить список инициализаторов в классе в конструкторе. Так что я думаю, что в конце это может быть очень запутанным.
struct Foo
{
Foo() {} // str = "test\0";
// Implementing this is easier if I can clearly see how big `str` is,
Foo() : str({'a','b', 'c', 'd'}) {} // str = "abcd0"const char str[] = "test";
};
Обратите внимание, что замена const char
с static constexpr char
работает отлично, и, вероятно, это то, что вы хотите в любом случае.
Если компилятору было разрешено поддерживать то, что вы описали, и размер str
был выведен 5
,
Foo foo = {{"This is not a test"}};
приведет к неопределенному поведению.
Как упомянуто в комментариях и как ответил @sbabbi, ответ заключается в деталях
12.6.2 Инициализация баз и членов [class.base.init]
В не делегирующем конструкторе, если данный нестатический элемент данных или
базовый класс не обозначается идентификатором mem-initializer-id (включая
случай, когда нет mem-initializer-list, потому что конструктор
не имеет инициализатора ctor), и объект не является виртуальным базовым классом
абстрактный класс (10.4), тогда
- если объект является нестатическим элементом данных, который имеет инициализатор с фигурной или равной скобкой, объект инициализируется, как указано в
8,5;- в противном случае, если объект является анонимным объединением или вариантом члена (9.5), инициализация не выполняется;
- в противном случае объект инициализируется по умолчанию
12.6.2 Инициализация баз и членов [class.base.init]
Если данный нестатический элемент данных имеет
инициализатор скобок или равных и инициализатор mem, инициализация
заданный mem-инициализатор выполняется, и нестатические данные
инициализация-член-скобка игнорируется. [Пример: даноstruct A { int i = /∗ some integer expression with side effects ∗/ ; A(int arg) : i(arg) { } // ... };
конструктор A (int) просто инициализирует i значением arg,
и побочные эффекты в I Brace- или Equal-Initializer не будут принимать
место. — конец примера]
Таким образом, если существует не удаляющий конструктор, инициализатор фигурных скобок игнорируется, и инициализация внутри элемента конструктора преобладает. Таким образом, для элементов массива, размер которых опущен, выражение становится некорректным. §12.6.2, пункт 9, делает это более явным, когда мы указали, что выражение инициализатора r-значения опущено, если mem-initialization выполняется конструктором.
Кроме того, обсуждение группы Google Еще одно несоответствующее поведение в C ++, дорабатывает и делает его более ясным. Это расширяет идею объяснения, что инициализация-скобка или равный-инициализатор является прославленным способом инициализации в элементе для случаев, когда инициализация в элементе для элемента не существует. В качестве примера
struct Foo {
int i[5] ={1,2,3,4,5};
int j;
Foo(): j(0) {};
}
эквивалентно
struct Foo {
int i[5];
int j;
Foo(): j(0), i{1,2,3,4,5} {};
}
но теперь мы видим, что если бы размер массива был опущен, выражение было бы неправильно сформировано.
Но с другой стороны, компилятор мог бы поддерживать эту функцию в тех случаях, когда элемент не инициализируется инициализацией конструктора в элементе, но в настоящее время ради единообразия стандарт, как и многие другие, не поддерживает эту функцию.