В чем причина невозможности определить размер массива из строки инициализатора в переменной-члене?

Рассмотрим код:

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-подобная нулевая завершенная строка.

3 ответа

Решение

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

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 работает отлично, и, вероятно, это то, что вы хотите в любом случае.

Как упомянуто в комментариях и как ответил @sbabbi, ответ заключается в деталях

12.6.2 Инициализация баз и членов [class.base.init]

  1. В не делегирующем конструкторе, если заданный нестатический элемент данных или базовый класс не обозначен с помощью mem-initializer-id (включая случай, когда нет mem-initializer-list, потому что конструктор не имеет ctor-initializer) и сущность не является виртуальным базовым классом абстрактного класса (10.4), то

    • если объект является нестатическим элементом данных, который имеет инициализатор с фигурной или равной скобкой, объект инициализируется, как указано в 8.5;
    • в противном случае, если объект является анонимным объединением или вариантом члена (9.5), инициализация не выполняется;
    • в противном случае объект инициализируется по умолчанию

12.6.2 Инициализация баз и членов [class.base.init]

  1. Если данный элемент нестатических данных имеет инициализатор скобок или равных и инициализатор mem, выполняется инициализация, заданная инициализатором mem, и инициализатор скобок или равных равен: игнорируются. [Пример: дано

    struct A { 
        int i = /∗ some integer expression with side effects ∗/ ; 
        A(int arg) : i(arg) { } 
        // ... 
    };
    

конструктор A(int) просто инициализирует i значением arg, и побочные эффекты в скобках или равных инициализаторах i не будут иметь места. - конец примера]

Таким образом, если существует не удаляющий конструктор, инициализатор фигурной скобки или равный игнорируется, и инициализация внутри элемента конструктора преобладает. Таким образом, для элементов массива, размер которых опущен, выражение становится некорректным. §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} {};
}

но теперь мы видим, что если бы размер массива был опущен, выражение было бы неправильно сформировано.

Но с другой стороны, компилятор мог бы поддерживать эту функцию в тех случаях, когда элемент не инициализируется инициализацией конструктора внутри элемента, но в настоящее время в целях единообразия стандарт, как и многие другие, не поддерживает эту функцию.

Если компилятору было разрешено поддерживать то, что вы описали, и размер str был выведен 5,

Foo foo = {{"This is not a test"}};

приведет к неопределенному поведению.

Другие вопросы по тегам