Подтвердить что ты не робот

В какой степени С++ - статически типизированный язык?

Раньше я думал, что ответ на этот вопрос "100%", но я недавно указал на пример, который заставляет задуматься дважды. Рассмотрим массив C, объявленный как объект с автоматической продолжительностью хранения:

int main()
{
    int foo[42] = { 0 };
}

Здесь тип foo явно int[42]. Рассмотрим вместо этого этот случай:

int main()
{
    int* foo = new int[rand() % 42];
    delete[] foo;
}

Здесь тип foo равен int*, но как определить тип объекта, созданного выражением new во время компиляции? (Акцент делается на том, что я не говорю о указателе, возвращаемом выражением new, а скорее о объекте массива, созданном выражением new).

Это то, что параграф 5.3.4/1 стандарта С++ 11 указывает на результат выражения new:

[...] Объекты, созданные новым выражением, имеют динамическую продолжительность хранения (3.7.4). [Примечание: время жизни таких сущность не обязательно ограничивается областью, в которой она создана. -end note] Если объект не является массивом object, новое выражение возвращает указатель на созданный объект. Если это массив, новое выражение возвращает указатель на исходный элемент массива.

Раньше я думал, что в С++ тип всех объектов определяется во время компиляции, но приведенный выше пример, похоже, опровергает эту веру. Кроме того, в пункте 1.8/1:

[...] Свойства объекта определяются , когда объект создается. Объект может иметь имя (раздел 3). Объект имеет продолжительность хранения (3.7), которая влияет на его время жизни (3.8). Объект имеет тип (3.9). [...]

Итак, мои вопросы:

  • Что подразумевается под "свойствами" в последнем цитируемом абзаце? Очевидно, что имя объекта не может считаться чем-то, что определено "когда объект создан" - если "созданный" здесь не означает нечто иное, чем я думаю;
  • Существуют ли другие примеры объектов, тип которых определяется только во время выполнения?
  • Насколько корректно говорить, что С++ - это статически типизированный язык? Вернее, что является самым правильным способом классификации С++ в этом отношении?

Было бы здорово, если бы кто-нибудь мог разработать хотя бы один из вышеуказанных пунктов.

EDIT:

Стандарт, похоже, дает понять, что выражение new действительно создает объект массива, а не только несколько объектов, выложенных как массив, как указано некоторыми. В пункте 5.3.4/5 (любезно предоставлено Xeo):

Когда выделенным объектом является массив (т.е. используется синтаксис noptr-new-declarator или идентификатор нового типа или type-id обозначает тип массива), новое выражение дает указатель на исходный элемент (если есть) массива. [Примечание: оба new int и new int[10] имеют тип int*, а тип new int[i][10] - int (*)[10]-end note] Атрибут-specifier-seq в noptr-new-declarator содержит для связанного типа массива.

4b9b3361

Ответ 1

Термины "статический тип" и "динамический тип" относятся к выражениям.

статический тип

тип выражения (3.9), полученный в результате анализа программы без рассмотрения семантики выполнения


динамический тип

<glvalue> тип самого производного объекта (1.8), к которому glvalue, обозначенное выражением glvalue, относится к

Кроме того, вы можете видеть, что динамический тип отличается от статического типа, когда может быть получен статический тип, что означает, что тип динамического массива всегда совпадает с типом выражения static.

Итак, ваш вопрос:

но как определить тип объекта, созданного новым выражением во время компиляции?

Объекты имеют типы, но они не являются "статическими" или "динамическими" типами, не содержат выражения, которые относятся к объекту. Учитывая выражение, статический тип всегда известен во время компиляции. В отсутствие вывода динамический тип такой же, как у статического типа.

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

template<typename T>
T *create_array(size_t s) {
    switch(s) {
        case 1: return &(*new std::array<T, 1>)[0];
        case 2: return &(*new std::array<T, 2>)[0];
        // ...
    }
}

Здесь мало особых или уникальных. Другая возможность:

struct B { virtual ~B() {}};
struct D : B {};
struct E : B {};

B *create() {
    if (std::bernoulli_distribution(0.5)(std::default_random_engine())) {
        return new D;
    }
    return new E;
}

Или:

void *create() {
    if (std::bernoulli_distribution(0.5)(std::default_random_engine())) {
        return reinterpret_cast<void*>(new int);
    }
    return reinterpret_cast<void*>(new float);
}

Единственное отличие от new int[] заключается в том, что вы не можете видеть в его реализации, чтобы видеть, как он выбирает различные типы объектов для создания.

Ответ 2

Новое выражение не создает объект с типом массива, зависящим от времени выполнения. Он создает много объектов, каждый из которых имеет статический тип int. Количество этих объектов неизвестно статически.


С++ предоставляет два случая (раздел 5.2.8) для динамического типа:

  • То же, что и статический тип выражения
  • Когда статический тип является полиморфным, тип времени выполнения самого производного объекта

Ни один из них не дает никакого объекта, созданного типом динамического массива new int[N].


Педантично оценка нового выражения создает бесконечное количество перекрывающихся объектов массива. Из 3.8p2:

[Примечание: время жизни объекта массива начинается, как только будет получено хранилище с надлежащим размером и выравниванием, а его время жизни заканчивается, когда хранилище, которое занимает массив, повторно используется или освобождается. 12.6.2 описывает время жизни базовых и членных подобъектов. - конечная нота]

Итак, если вы хотите поговорить о "объекте массива", созданном new int[5], вы должны указать ему не только тип int[5], но также int[4], int[1], char[5*sizeof(int)] и struct s { int x; }[5].

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

Ответ 3

Раньше я думал, что в С++ тип всех объектов определяется во время компиляции, но приведенный выше пример, похоже, опровергает эту веру.

В примере, который вы цитируете, говорится о продолжительности хранения элемента. С++ распознает три длительности хранения:

  • Статическая продолжительность хранения - это длительность глобальных и локальных статических переменных.
  • Автоматическая продолжительность хранения - это продолжительность для локальных переменных "распределенная стек".
  • Длительность динамического хранения - это продолжительность для динамически распределенной памяти, например, с new или malloc.

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

Раньше я думал, что в С++ тип всех объектов определяется во время компиляции, но приведенный выше пример, похоже, опровергает эту веру.

В вашем примере есть одна переменная, которая имеет тип int*. Нет фактического типа массива для базового массива, который может быть восстановлен любым значимым образом в программе. Не происходит динамического ввода.