Annex C (informative) Compatibility [diff]

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

C.5.6 [dcl.dcl]: declarations [diff.dcl]

Affected subclause: [dcl.stc]
Change: In C++, the static or extern specifiers can only be applied to names of objects or functions.

Using these specifiers with type declarations is illegal in C++.
In C, these specifiers are ignored when used on type declarations.
Example: static struct S { // valid C, invalid in C++ int i; };

Rationale: Storage class specifiers don't have any meaning when associated with a type.
In C++, class members can be declared with the static storage class specifier.
Allowing storage class specifiers on type declarations could render the code confusing for users.

Effect on original feature: Deletion of semantically well-defined feature.

Difficulty of converting: Syntactic transformation.

How widely used: Seldom.
Affected subclause: [dcl.stc]
Change: In C++, register is not a storage class specifier.

Rationale: The storage class specifier had no effect in C++.

Effect on original feature: Deletion of semantically well-defined feature.

Difficulty of converting: Syntactic transformation.

How widely used: Common.
Affected subclause: [dcl.typedef]
Change: A C++ typedef name must be different from any class type name declared in the same scope (except if the typedef is a synonym of the class name with the same name).
In C, a typedef name and a struct tag name declared in the same scope can have the same name (because they have different name spaces).
Example: typedef struct name1 { /* ... */ } name1; // valid C and C++ struct name { /* ... */ }; typedef int name; // valid C, invalid C++

Rationale: For ease of use, C++ doesn't require that a type name be prefixed with the keywords class, struct or union when used in object declarations or type casts.
Example: class name { /* ... */ }; name i; // i has type class name

Effect on original feature: Deletion of semantically well-defined feature.

Difficulty of converting: Semantic transformation.
One of the 2 types has to be renamed.

How widely used: Seldom.
Affected subclause: [dcl.type] [see also [basic.link]]
Change: Const objects must be initialized in C++ but can be left uninitialized in C.
Rationale: A const object cannot be assigned to so it must be initialized to hold a useful value.

Effect on original feature: Deletion of semantically well-defined feature.

Difficulty of converting: Semantic transformation.

How widely used: Seldom.
Affected subclause: [dcl.type]
Change: Banning implicit int.
In C++ a decl-specifier-seq must contain a type-specifier, unless it is followed by a declarator for a constructor, a destructor, or a conversion function.
In the following example, the left-hand column presents valid C; the right-hand column presents equivalent C++: void f(const parm); void f(const int parm); const n = 3; const int n = 3; main() int main() /* ... */ /* ... */

Rationale: In C++, implicit int creates several opportunities for ambiguity between expressions involving function-like casts and declarations.
Explicit declaration is increasingly considered to be proper style.
Liaison with WG14 (C) indicated support for (at least) deprecating implicit int in the next revision of C.
Effect on original feature: Deletion of semantically well-defined feature.

Difficulty of converting: Syntactic transformation.
Could be automated.

How widely used: Common.
Affected subclause: [dcl.spec.auto]
Change: The keyword auto cannot be used as a storage class specifier.
void f() { auto int x; // valid C, invalid C++ }

Rationale: Allowing the use of auto to deduce the type of a variable from its initializer results in undesired interpretations of auto as a storage class specifier in certain contexts.

Effect on original feature: Deletion of semantically well-defined feature.

Difficulty of converting: Syntactic transformation.

How widely used: Rare.
Affected subclause: [dcl.fct]
Change: In C++, a function declared with an empty parameter list takes no arguments.
In C, an empty parameter list means that the number and type of the function arguments are unknown.
Example: int f(); // means int f(void) in C++ // int f( unknown ) in C

Rationale: This is to avoid erroneous function calls (i.e., function calls with the wrong number or type of arguments).

Effect on original feature: Change to semantics of well-defined feature.
This feature was marked as “obsolescent” in C.
Difficulty of converting: Syntactic transformation.
The function declarations using C incomplete declaration style must be completed to become full prototype declarations.
A program may need to be updated further if different calls to the same (non-prototype) function have different numbers of arguments or if the type of corresponding arguments differed.

How widely used: Common.
Affected subclause: [dcl.fct] [see [expr.sizeof]]
Change: In C++, types may not be defined in return or parameter types.
In C, these type definitions are allowed.
Example: void f( struct S { int a; } arg ) {} // valid C, invalid C++ enum E { A, B, C } f() {} // valid C, invalid C++

Rationale: When comparing types in different translation units, C++ relies on name equivalence when C relies on structural equivalence.
Regarding parameter types: since the type defined in a parameter list would be in the scope of the function, the only legal calls in C++ would be from within the function itself.

Effect on original feature: Deletion of semantically well-defined feature.

Difficulty of converting: Semantic transformation.
The type definitions must be moved to file scope, or in header files.

How widely used: Seldom.
This style of type definition is seen as poor coding style.
Affected subclause: [dcl.fct.def]
Change: In C++, the syntax for function definition excludes the “old-style” C function.
In C, “old-style” syntax is allowed, but deprecated as “obsolescent”.

Rationale: Prototypes are essential to type safety.

Effect on original feature: Deletion of semantically well-defined feature.

Difficulty of converting: Syntactic transformation.

How widely used: Common in old programs, but already known to be obsolescent.
Affected subclause: [dcl.init.aggr]
Change: In C++, designated initialization support is restricted compared to the corresponding functionality in C.
In C++, designators for non-static data members must be specified in declaration order, designators for array elements and nested designators are not supported, and designated and non-designated initializers cannot be mixed in the same initializer list.
Example: struct A { int x, y; }; struct B { struct A a; }; struct A a = {.y = 1, .x = 2}; // valid C, invalid C++ int arr[3] = {[1] = 5}; // valid C, invalid C++ struct B b = {.a.x = 0}; // valid C, invalid C++ struct A c = {.x = 1, 2}; // valid C, invalid C++
Rationale: In C++, members are destroyed in reverse construction order and the elements of an initializer list are evaluated in lexical order, so field initializers must be specified in order.
Array designators conflict with lambda-expression syntax.
Nested designators are seldom used.

Effect on original feature: Deletion of feature that is incompatible with C++.

Difficulty of converting: Syntactic transformation.

How widely used: Out-of-order initializers are common.
The other features are seldom used.
Affected subclause: [dcl.init.string]
Change: In C++, when initializing an array of character with a string, the number of characters in the string (including the terminating '\0') must not exceed the number of elements in the array.
In C, an array can be initialized with a string even if the array is not large enough to contain the string-terminating '\0'.
Example: char array[4] = "abcd"; // valid C, invalid C++
Rationale: When these non-terminated arrays are manipulated by standard string functions, there is potential for major catastrophe.

Effect on original feature: Deletion of semantically well-defined feature.

Difficulty of converting: Semantic transformation.
The arrays must be declared one element bigger to contain the string terminating '\0'.

How widely used: Seldom.
This style of array initialization is seen as poor coding style.
Affected subclause: [dcl.enum]
Change: C++ objects of enumeration type can only be assigned values of the same enumeration type.
In C, objects of enumeration type can be assigned values of any integral type.
Example: enum color { red, blue, green }; enum color c = 1; // valid C, invalid C++

Rationale: The type-safe nature of C++.

Effect on original feature: Deletion of semantically well-defined feature.

Difficulty of converting: Syntactic transformation.
(The type error produced by the assignment can be automatically corrected by applying an explicit cast.)

How widely used: Common.
Affected subclause: [dcl.enum]
Change: In C++, the type of an enumerator is its enumeration.
In C, the type of an enumerator is int.
Example: 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) */

Rationale: In C++, an enumeration is a distinct type.

Effect on original feature: Change to semantics of well-defined feature.

Difficulty of converting: Semantic transformation.

How widely used: Seldom.
The only time this affects existing C code is when the size of an enumerator is taken.
Taking the size of an enumerator is not a common C coding practice.