c ++ 11 — В какой степени C ++ является статически типизированным языком?

Раньше я думал, что ответ на этот вопрос был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 стандарта C ++ 11 указывает на результат new выражение:

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

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

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

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

  1. Что подразумевается подсвойства«в последнем цитируемом абзаце? Очевидно, что имя объекта не может считаться чем-то определенным»когда объект создан«- если только»созданный«здесь означает нечто иное, чем я думаю;
  2. Существуют ли другие примеры объектов, тип которых определяется только во время выполнения?
  3. Насколько правильно говорить, что C ++ является языком статической типизации? Или, скорее, каков наиболее правильный способ классификации C ++ в этом отношении?

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

РЕДАКТИРОВАТЬ:

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

Когда выделенный объект является массивом (это noptr новый-описатель Синтаксис используется или новый тип-идентификатор или же
тип-идентификатор обозначает тип массива), новое выражение возвращает указатель на начальный элемент (если есть) массива.
[Примечание: оба new int а также new int[10] иметь тип int* и тип new int[i][10] является int (*)[10]
— Конец примечания] Атрибут спецификатор-сл в noptr новый-описатель appertains в связанный тип массива.

21

Решение

Термины «статический тип» и «динамический тип» применяются к выражениям.

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

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

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

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

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

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

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

У объектов есть типы, но они не являются «статическими» или «динамическими» типами, и в них отсутствует выражение, относящееся к объекту. Для данного выражения статический тип всегда известен во время компиляции. При отсутствии деривации динамический тип совпадает со статическим типом.

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

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[] в том, что вы не можете заглянуть в его реализацию, чтобы увидеть выбор между различными типами объектов для создания.

8

Другие решения

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


C ++ предоставляет два случая (раздел 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],

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

9

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

В приведенном вами примере говорится о сроке хранения предмета. C ++ распознает три длительности хранения:

  1. Статическая длительность хранения — это длительность глобальных и локальных статических переменных.
  2. Длительность автоматического хранения — это длительность локальных переменных «распределенных в стеке».
  3. Динамическая продолжительность хранения — это продолжительность динамически выделяемой памяти, например, с new или же malloc,

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

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

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

2
По вопросам рекламы [email protected]