Annex C (informative) Compatibility [diff]

C.1 C++ and ISO C [diff.iso]

В этом подпункте перечислены различия между C ++ и ISO C по главам этого документа.

C.1.1 Clause [lex]: lexical conventions [diff.lex]

[lex.key]
Изменение: новые ключевые
слова В C ++ добавлены новые ключевые слова; см[lex.key].
Обоснование: эти ключевые слова были добавлены для реализации новой семантики C ++.
Влияние на исходную функцию: изменение семантики четко определенной функции. Любые программы ISO C, которые использовали любое из этих ключевых слов в качестве идентификаторов, не являются допустимыми программами C ++.
Сложность преобразования: синтаксическое преобразование. Преобразовать одну конкретную программу очень просто. Преобразование большого набора связанных программ требует больше работы.
Насколько широко используется: Обычный.

[lex.ccon]
Изменение: Тип символьного литерала изменен сint наchar.
Обоснование: это необходимо для улучшенного сопоставления типов аргументов перегруженной функции. Например:

int function( int i );
int function( char c );

function( 'x' );

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

sizeof('x') == sizeof(int)

не будет работать так же, как программы на C ++.
Сложность конвертации: Простая.
Насколько широко используется: Зависящие отsizeof('x') него программы, вероятно, встречаются редко.

Подпункт[lex.string]:
Изменение: строковые литералы сделаны константными.
Тип строкового литерала изменяется с «массивchar» на «массив изconst char». Типchar16_­t строкового литерала изменяется с «массивsome-integer-type» на «массив изconst char16_­t». Типchar32_­t строкового литерала изменяется с «массивsome-integer-type» на «массив изconst char32_­t». Тип широкого строкового литерала изменяется с «массивwchar_­t» на «массив изconst wchar_­t».
Обоснование: это позволяет избежать вызова несоответствующей перегруженной функции, которая может рассчитывать на изменение своего аргумента.
Влияние на исходную функцию: изменение семантики четко определенной функции.
Сложность преобразования: синтаксическое преобразование. Исправление состоит в том, чтобы добавить приведение:

char* p = "abc";                // valid in C, invalid in C++
void f(char*) {
  char* p = (char*)"abc";       // OK: cast added
  f(p);
  f((char*)"def");              // OK: cast added
}


Насколько широко используется: программы, у которых есть законные основания рассматривать строковые литералы как указатели на потенциально изменяемую память, вероятно, встречаются редко.

C.1.2 Clause [basic]: basic concepts [diff.basic]

[basic.def]
Изменение: C ++ не имеет «предварительных определений», как в C.
Например, в области видимости файла,

int i;
int i;

допустимо в C, недопустимо в C ++. Это делает невозможным определение взаимно ссылочных локальных статических объектов файла, если инициализаторы ограничены синтаксическими формами C.Например,

struct X { int i; struct X* next; };

static struct X a;
static struct X b = { 0, &a };
static struct X a = { 1, &b };


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

[basic.scope]
Изменение: Astruct - это область видимости в C ++, а не C.
Обоснование: область видимости класса имеет решающее значение для C ++, а структура - это класс.
Влияние на исходную функцию: изменение семантики четко определенной функции.
Сложность преобразования: семантическое преобразование.
Насколько широко используются: программы на C используютсяstruct чрезвычайно часто, но изменение заметно только тогдаstruct, когда имена перечисления или перечислителя упоминаются внеstruct. Последнее, вероятно, встречается редко.

[basic.link] [также[dcl.type]]
Изменение: имя области видимости файла, которое явно объявленоconst, а не объявлено явноextern, имеет внутреннюю связь, тогда как в C оно будет иметь внешнюю связь.
Обоснование: посколькуconst объекты могут использоваться в качестве значений во время трансляции в C ++, эта функция побуждает программистов предоставлять явный инициализатор для каждогоconst объекта. Эта функция позволяет пользователю помещатьconst объекты в исходные файлы, которые включены более чем в одну единицу перевода.
Влияние на исходную функцию: изменение семантики четко определенной функции.
Сложность преобразования: семантическое преобразование.
Насколько широко используется: редко.

[basic.start.main]
Изменение:main функция не может вызываться рекурсивно и не может быть его адрес взят.
Обоснование:main функция может потребовать специальных действий.
Влияние на исходный объект: удаление семантически четко определенного объекта.
Сложность конвертации: Тривиально: создайте посредническую функцию, такую ​​как mymain(argc, argv).
Насколько широко используется: редко.

[basic.types]
Изменение: C допускает «совместимые типы» в нескольких местах, а C ++ - нет.
Например, идентичныеstruct типы с разными именами тегов «совместимы» в C, но являются совершенно разными типами в C ++.
Обоснование: для C ++ необходима более строгая проверка типов.
Влияние на исходный объект: удаление семантически четко определенного объекта.
Сложность преобразования: семантическое преобразование. Механизм «безопасного соединения» обнаружит множество, но не все, таких проблем. Те проблемы, которые не обнаруживаются с помощью безопасного связывания, будут продолжать функционировать должным образом в соответствии с «правилами совместимости компоновки» настоящего международного стандарта.
Насколько широко используется: Обычный.

C.1.3 Clause [conv]: standard conversions [diff.conv]

[conv.ptr]
Изменение: для преобразованияvoid* в тип указателя на объект требуется приведение типов.

char a[10];
void* b=a;
void foo() {
  char* c=b;
}

ISO C примет такое использование указателя на void, присвоенного указателю на тип объекта. C ++ не будет.
Обоснование: C ++ больше старается, чем C, обеспечить безопасность типов во время компиляции.
Влияние на исходный объект: удаление семантически четко определенного объекта.
Сложность конвертации: Можно автоматизировать. Нарушения диагностирует переводчик C ++. Исправление заключается в добавлении литья. Например:

char* c = (char*) b;


Насколько широко используется: это довольно широко используется, но хорошая практика программирования - добавлять приведение при назначении указателя на пустоту для указателя на объект. Некоторые переводчики ISO C выдают предупреждение, если приведение не используется.

C.1.4 Clause [expr]: expressions [diff.expr]

[expr.call]
Изменение: Неявное объявление функций запрещено.
Обоснование: типобезопасная природа C ++.
Влияние на исходный объект: удаление семантически четко определенного объекта. Примечание: исходная функция была помечена как «устаревшая» в ISO C.
Сложность преобразования: синтаксическое преобразование. Средства для создания явных объявлений функций довольно широко распространены на рынке.
Насколько широко используется: Обычный.

[expr.post.incr], Изменение: оператор декремента не может использоваться с операндом. Обоснование: функция с удивительной семантикой. Влияние на исходную функцию: допустимое выражение ISO C, использующее оператор декремента для lvalue (например, через C typedef in ), неправильно сформировано в этом международном стандарте.[expr.pre.incr]
bool

bool <stdbool.h>

[expr.sizeof], Изменение: Типы должны определяться в объявлениях, а не в выражениях. В C выражение sizeof или выражение приведения может определять новый тип. Например, [expr.cast]

p = (void*)(struct x {int i;} *)0;

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

[expr.cond],[expr.ass], Изменение: Результат условного выражения, выражений присваивания или выражений запятой может быть именующим. Обоснование: C ++ - объектно-ориентированный язык, в котором относительно больше внимания уделяется lvalue. Например, функции могут возвращать lvalue. Влияние на исходную функцию: изменение семантики четко определенной функции. Некоторые выражения C, которые неявно полагаются на преобразования lvalue-to-rvalue, дадут разные результаты. Например,[expr.comma]


char arr[100];
sizeof(0, arr)

дает 100 в C ++ и sizeof(char*) C.
Сложность преобразования: программы должны добавлять явное приведение к соответствующему rvalue.
Насколько широко используется: редко.

C.1.5 Clause [stmt.stmt]: statements [diff.stat]

[stmt.switch], Изменение: теперь невозможно пропустить объявление с явным или неявным инициализатором (за исключением всего не введенного блока). Обоснование: Конструкторы, используемые в инициализаторах, могут выделять ресурсы, которые необходимо отменить при выходе из блока. Разрешение перехода за пределы инициализаторов потребует сложного определения распределения во время выполнения. Более того, любое использование неинициализированного объекта может привести к катастрофе. С помощью этого простого правила времени компиляции C ++ гарантирует, что если инициализированная переменная находится в области видимости, то она обязательно была инициализирована. Влияние на исходный объект: удаление семантически четко определенного объекта. Сложность преобразования: семантическое преобразование. Насколько широко используется: редко.[stmt.goto]




[stmt.return]
Изменение: теперь недопустимо возвращать (явно или неявно) из функции, которая, как объявлено, возвращает значение без фактического возврата значения.
Обоснование: вызывающий и вызываемый могут предполагать довольно сложные механизмы возврата для возврата объектов класса. Если некоторые пути потока выполняют возврат без указания какого-либо значения, реализация должна включать в себя гораздо больше сложностей. Кроме того, обещание вернуть значение заданного типа, а затем не возвращать такое значение, всегда считалось сомнительной практикой, допускаемой только потому, что в очень старом C не было различий между функциями void и функциями int.
Влияние на исходный объект: удаление семантически четко определенного объекта.
Сложность преобразования: семантическое преобразование. Добавьте в исходный код соответствующее возвращаемое значение, например ноль.
Насколько широко используется: редко. В течение нескольких лет многие существующие реализации C выдавали предупреждения в этом случае.

C.1.6 Clause [dcl.dcl]: declarations [diff.dcl]

[dcl.stc]
Изменение: В C ++ спецификаторыstatic илиextern могут применяться только к именам объектов или функций.
Использование этих спецификаторов с объявлениями типов недопустимо в C ++. В C эти спецификаторы игнорируются при использовании в объявлениях типов.

Пример:

static struct S {               // valid C, invalid in C++
  int i;
};


Обоснование: спецификаторы класса хранилища не имеют никакого значения, когда они связаны с типом. В C ++ члены класса могут быть объявлены с помощьюstatic спецификатора класса хранения. Разрешение указателей класса хранения в объявлениях типов может сбить с толку пользователей.
Влияние на исходный объект: удаление семантически четко определенного объекта.
Сложность преобразования: синтаксическое преобразование.
Насколько широко используется: редко.

[dcl.stc]
Изменение: в C ++register не является спецификатором класса хранения.
Обоснование: спецификатор класса хранения не действует в C ++.
Влияние на исходный объект: удаление семантически четко определенного объекта.
Сложность преобразования: синтаксическое преобразование.
Насколько широко используется: Обычный.

[dcl.typedef]
Изменение: имя typedef C ++ должно отличаться от любого имени типа класса, объявленного в той же области (кроме случаев, когда typedef является синонимом имени класса с тем же именем). В C имя typedef и имя тега структуры, объявленные в одной области, могут иметь одно и то же имя (потому что у них разные пространства имен).

Пример:

typedef struct name1 { /* ... */ } name1;         // valid C and C++
struct name { /* ... */ };
typedef int name;               // valid C, invalid C++


Обоснование: Для простоты использования, C ++ не требует , чтобы имя типа с префиксом ключевых словclass,struct илиunion при использовании в объявлениях объектов или слепков типа.

Пример:

class name { /* ... */ };
name i;                         // i has type class name


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

[dcl.type] [см. также[basic.link]]
Изменение:const объекты должны быть инициализированы в C ++, но могут быть оставлены неинициализированными в C.
Обоснование: объект const не может быть назначен, поэтому он должен быть инициализирован для хранения полезного значения.
Влияние на исходный объект: удаление семантически четко определенного объекта.
Сложность преобразования: семантическое преобразование.
Насколько широко используется: редко.

[dcl.type]
Изменение: неявный запретint.

В C ++ a decl-specifier-seq должен содержать a type-specifier, если за ним не следует декларатор для конструктора, деструктора или функции преобразования. В следующем примере в левом столбце представлен действительный C; в правом столбце представлен эквивалентный C ++:

void f(const parm);            void f(const int parm);
const n = 3;                   const int n = 3;
main()                         int main()
    /* ... */                      /* ... */


Обоснование: В C ++ неявное int создает несколько возможностей для двусмысленности между выражениями, включающими функционально-подобные приведения и объявления. Явное объявление все чаще считается правильным стилем. Связь с WG14 (C) указала на поддержку (по крайней мере) отказа от неявного int в следующей версии C.
Влияние на исходную функцию: удаление семантически четко определенной функции.
Сложность преобразования: синтаксическое преобразование. Можно автоматизировать.
Насколько широко используется: Обычный.

[dcl.spec.auto]
Изменение: ключевое словоauto нельзя использовать в качестве спецификатора класса хранения.

void f() {
  auto int x;     // valid C, invalid C++
}


Обоснование: разрешение использоватьauto для определения типа переменной из ее инициализатора приводит к нежелательной интерпретации auto в качестве спецификатора класса хранения в определенных контекстах.
Влияние на исходный объект: удаление семантически четко определенного объекта.
Сложность преобразования: синтаксическое преобразование.
Насколько широко используется: редко.

[dcl.enum]
Изменение: объектам C ++ перечислимого типа могут быть присвоены значения только одного и того же перечислимого типа. В C объектам перечислимого типа могут быть присвоены значения любого целочисленного типа.

Пример:

enum color { red, blue, green };
enum color c = 1;               // valid C, invalid C++


Обоснование: типобезопасная природа C ++.
Влияние на исходный объект: удаление семантически четко определенного объекта.
Сложность преобразования: синтаксическое преобразование. (Ошибка типа, вызванная назначением, может быть автоматически исправлена ​​путем применения явного приведения.)
Насколько широко используется: Часто.

[dcl.enum]
Изменение: В C ++ тип перечислителя - это его перечисление. В C тип перечислителяint.

Пример:

enum e { A };
sizeof(A) == sizeof(int)        // in C
sizeof(A) == sizeof(e)          // in C++
/* and sizeof(int) is not necessarily equal to sizeof(e) */


Обоснование: В C ++ перечисление - это отдельный тип.
Влияние на исходную функцию: изменение семантики четко определенной функции.
Сложность преобразования: семантическое преобразование.
Насколько широко используется: редко. Единственный раз, когда это влияет на существующий код C, - это когда берется размер перечислителя. Взятие размера перечислителя - не обычная практика кодирования C.

C.1.7 Clause [dcl.decl]: declarators [diff.decl]

[dcl.fct]
Изменение: в C ++ функция, объявленная с пустым списком параметров, не принимает аргументов. В C пустой список параметров означает, что количество и тип аргументов функции неизвестны.

Пример:

int f();            // means   int f(void) in C++
                    // int f( unknown ) in C


Обоснование: Это сделано для того, чтобы избежать ошибочных вызовов функций (т. Е. Вызовов функций с неправильным числом или типом аргументов).
Влияние на исходную функцию: изменение семантики четко определенной функции. Эта функция была отмечена как «устаревшая» в C.
Сложность преобразования: синтаксическое преобразование. Объявления функций, использующие стиль неполного объявления C, должны быть завершены, чтобы стать объявлениями полных прототипов. Программа может нуждаться в дальнейшем обновлении, если разные вызовы одной и той же (не являющейся прототипом) функции имеют разное количество аргументов или если тип соответствующих аргументов различается.
Насколько широко используется: Обычный.

[dcl.fct] [см.[expr.sizeof]]
Изменение: В C ++ типы не могут быть определены в возвращаемых типах или типах параметров. В C эти определения типов разрешены.

Пример:

void f( struct S { int a; } arg ) {}    // valid C, invalid C++
enum E { A, B, C } f() {}               // valid C, invalid C++


Обоснование: при сравнении типов в разных единицах перевода C ++ полагается на эквивалентность имен, тогда как C полагается на структурную эквивалентность. Что касается типов параметров: поскольку тип, определенный в списке параметров, будет находиться в области действия функции, единственные допустимые вызовы в C ++ будут из самой функции.
Влияние на исходный объект: удаление семантически четко определенного объекта.
Сложность преобразования: семантическое преобразование. Определения типов необходимо переместить в область файла или в файлы заголовков.
Насколько широко используется: редко. Такой стиль определения типа считается плохим стилем кодирования.

[dcl.fct.def]
Изменение: В C ++ синтаксис определения функции исключает функцию C «старого стиля». В C синтаксис «старого стиля» разрешен, но считается устаревшим.
Обоснование: прототипы необходимы для обеспечения безопасности типов.
Влияние на исходный объект: удаление семантически четко определенного объекта.
Сложность преобразования: синтаксическое преобразование.
Насколько широко используется: часто встречается в старых программах, но уже известно, что они устарели.

[dcl.init.string]
Изменение: В C ++ при инициализации массива символов строкой количество символов в строке (включая завершающие'\0') не должно превышать количество элементов в массиве. В C массив может быть инициализирован строкой, даже если массив недостаточно велик, чтобы содержать завершающую строку'\0'.

Пример:

char array[4] = "abcd";         // valid C, invalid C++


Обоснование: манипулирование этими незавершенными массивами стандартными строковыми функциями может привести к серьезной катастрофе.
Влияние на исходный объект: удаление семантически четко определенного объекта.
Сложность преобразования: семантическое преобразование. Массивы должны быть объявлены на один элемент больше, чтобы содержать завершающую строку'\0'.
Насколько широко используется: редко. Такой стиль инициализации массива считается плохим стилем кодирования.

C.1.8 Clause [class]: classes [diff.class]

[class.name] [см. также[dcl.typedef]]
Изменение: В C ++ объявление класса вводит имя класса в область, в которой он объявлен, и скрывает любой объект, функцию или другое объявление с этим именем во включающей области. В C объявление внутренней области видимости имени тега структуры никогда не скрывает имя объекта или функции во внешней области.

Пример:

int x[99];
void f() {
  struct x { int a; };
  sizeof(x);  /* size of the array in C */
  /* size of the struct in C++ */
}


Обоснование: это одна из немногих несовместимостей между C и C ++, которая может быть отнесена к новому определению пространства имен C ++, где имя может быть объявлено как тип и как не-тип в одной области, в результате чего имя, не являющееся типом, становится скрыть имя типа и требует, чтобы ключевые словаclass,struct,union илиenum использоваться для обозначения имени типа. Это новое определение пространства имен обеспечивает важные нотационные удобства для программистов на C ++ и помогает сделать использование определяемых пользователем типов как можно более похожим на использование фундаментальных типов. Было сочтено, что преимущества нового определения пространства имен намного перевешивают несовместимость с C, описанным выше.
Влияние на исходную функцию: изменение семантики четко определенной функции.
Сложность преобразования: семантическое преобразование. Если скрытое имя, к которому необходимо получить доступ, находится в глобальной области видимости,​::​ можно использовать оператор C ++. Если скрытое имя находится в области видимости блока, необходимо переименовать тип или тег структуры.
Насколько широко используется: редко.

[class.bit]
Изменение: Подписаны битовые поля типа plainint .
Обоснование: оставление выбора подписи на усмотрение реализаций может привести к несогласованным определениям специализаций шаблонов. Для согласованности также была исключена свобода реализации для независимых типов.
Влияние на исходную функцию: выбор определяется реализацией в C, но не в C ++.
Сложность преобразования: синтаксическое преобразование.
Насколько широко используется: редко.

[class.nest]
Изменение: В C ++ имя вложенного класса является локальным для включающего его класса. В C имя вложенного класса принадлежит той же области, что и имя самого внешнего включающего класса.

Пример:

struct X {
  struct Y { /* ... */ } y;
};
struct Y yy;                    // valid C, invalid C++


Обоснование: классы C ++ имеют функции-члены, которые требуют, чтобы классы устанавливали области действия. Правило C оставит классы как механизм неполной области видимости, который не позволит программистам на C ++ поддерживать локальность внутри класса. Согласованный набор правил области видимости для C ++, основанный на правиле C, был бы очень сложным, и программисты на C ++ не смогли бы надежно предсказать значения нетривиальных примеров, включающих вложенные или локальные функции.
Влияние на исходную функцию: изменение семантики четко определенной функции.
Сложность преобразования: семантическое преобразование. Чтобы сделать имя типа структуры видимым в области охватывающей структуры, тег структуры может быть объявлен в области охватывающей структуры до того, как будет определена включающая структура. Пример:

struct Y;                       // struct Y and struct X are at the same scope
struct X {
  struct Y { /* ... */ } y;
};

Все определения типов структур C, заключенные в другие определения структур и доступные за пределами области действия включающей структуры, могут быть экспортированы в область действия включающей структуры. Примечание: это следствие разницы в правилах области видимости, которая задокументирована в[basic.scope].
Насколько широко используется: редко.

[class.nested.type]
Изменение: В C ++ имя typedef не может быть повторно объявлено в определении класса после использования в этом определении.

Пример:

typedef int I;
struct S {
  I i;
  int I;                  // valid C, invalid C++
};


Обоснование: когда классы усложняются, разрешение такого переопределения после использования типа может создать путаницу для программистов на C ++ относительно того, что наI самом деле означает.
Влияние на исходный объект: удаление семантически четко определенного объекта.
Сложность преобразования: семантическое преобразование. Необходимо переименовать тип или член структуры.
Насколько широко используется: редко.

C.1.9 Clause [special]: special member functions [diff.special]

[class.copy]
Изменение: Копирование летучих объектов.

Неявно объявленный конструктор копии и неявно объявленный оператор присваивания копии не могут сделать копию изменчивого lvalue. Например, в ISO C действует следующее:

struct X { int i; };
volatile struct X x1 = {0};
struct X x2 = x1;               // invalid C++
struct X x3;
x3 = x1;                        // also invalid C++


Обоснование: Подробно обсуждалось несколько альтернатив. Изменение параметра на volatile const X& значительно усложнит создание эффективного кода для объектов класса. Обсуждение предоставления двух альтернативных сигнатур для этих неявно определенных операций вызвало безответные опасения по поводу создания двусмысленностей и усложнения правил, определяющих формирование этих операторов в соответствии с базами и членами.
Влияние на исходный объект: удаление семантически четко определенного объекта.
Сложность преобразования: семантическое преобразование. Если для копии требуется изменчивая семантика, необходимо предоставить конструктор или присваивание, объявленный пользователем. Если требуется энергонезависимая семантика,const_­cast можно использовать явную .
Насколько широко используется: редко.

C.1.10 Clause [cpp]: preprocessing directives [diff.cpp]

[cpp.predefined]
Изменение:__STDC__ определено ли, и если да, то каково его значение, определяется реализацией.
Обоснование: C ++ не идентичен ISO C. Обязательное__STDC__ определение потребует от переводчиков неверных утверждений. Каждая реализация должна выбрать поведение, которое будет наиболее полезным для ее рынка.
Влияние на исходную функцию: изменение семантики четко определенной функции.
Сложность преобразования: семантическое преобразование.
Насколько широко используется. Ссылки на программы и заголовки__STDC__ встречаются довольно часто.