ISO: WG21/N0358 ANSI: 93-0151 Author: John Max Skaller Date: 11/9/93 Reply to: maxtal@suphys.physics.us.oz.au GETTING RID OF DEFAULT INT -------------------------- PROPOSAL. --------- The following uses of default int are banned, all other uses are deprecated: 1) The C++ syntax is upgraded to agree with the ISO C syntax: declaration: decl-specifier-seq init-declaratior-list[opt] ; // more here and the words "only in function definitions and declarations may the decl-specifier-seq be omitted" are removed from the WP. Thus cases like f(int); x; are banned (as declarations) in C++ because they are not allowed in ISO C. (7) specifies this applied to function parameters: void f( (*)() ); // banned 2) anywhere in a typedef typedef Float; // banned: more likely an error than anything else typedef (*func)(); // banned: too confusing 3) anywhere in the scope of a 'template<>' // in the <> template ... // banned // in the declaration template f(T t); // banned template void f( (*)() ); // banned //in a definition template void f(){ const x; } // banned 4) anywhere in a friend declaration friend f(); // banned 5) anywhere after an inline declaration inline f(); // banned 6) anywhere after a virtual declaration virtual f(); // banned 7) for pointers to members X::*pmem = &X::anInt // banned (X::*pmem)() = &X::IntFunc() // banned 8) The same rules apply to function prototypes. Note that only (2) is a change from ISO C. (1) is actually a change TO the rules of ISO C from Classic C. DISCUSSION ---------- 1) The status quo: C -------------------- In C, the form f(); is not allowed, because the syntax declaration: declaration-specifiers init-declaratior-list[opt] ; declaration-specifiers: storage-class-specifier declaration-specifiers[opt] type-specifier declaration-specifiers[opt] type-qualifier declaration-specifiers[opt] clearly requires either a type-specifier, storage-class-specifier or type-qualifier. However the declarations typedef Double; // means typedef int Double; static f(); extern f(); const f(); static s; const x; are all allowed in C. 2) The status quo: C++ ---------------------- However, the C++ syntax rules (as in N0218, pre-Portland mailing) are declaration: decl-specifier-seq[opt] init-declaratior-list[opt] ; // more here and the words "only in function definitions and declarations may the decl-specifier-seq be omitted" appear. Thus in C++ f(); is allowed as a declaration. RECOMMENDATION: If nothing else is accepted, the C++ syntax should be changed to declaration: decl-specifier-seq init-declaratior-list[opt] ; // more here and the extra words quoted above elided, so that the C++ syntax agrees with C for C constructs, effectively banning the forms f(); f(int); as declarations. 3. Why we dont like default int -------------------------------- a) Its not the expected default ------------------------------- Since the default parameter declaration for functions has been changed so that void f(); means void f(void); and as the use of procedural methods without return type is common in C++, programmers are often surprised by diagnostics indicating that no return statement has been specified for a function with int return type, where the return type was defaulted to 'int'. Programmers may be even more surprised by run-time errors or weird behaviour when they intend a 'void' return type and the compiler does not detect a missing return statement. Subsequent use of the return value may access uninitialised store and have unpredicatable results. b) Difficulty of detecting syntax errors ---------------------------------------- Default int makes error detection difficult in cases such as typedef Float; // woops, Float is synonym for int and makes parsing for both human readers and compilers. The core group had a very long debate on name lookup, and for the first year it seemed like every second (or more) tricky example they came up with actually boiled down to a variation on the following: struct S { void f(const T); // what is T typedef double T; }; I.e., the difficulty would be a non-issue if we got rid of implicit int, because then we would know that T must be a type. The problem is even more significant for tools that need to parse C++: these are made much more complex by having to parse default int. As a result, the quality and availability of tools for C++ is significantly impacted by allowing default int. c) Complication of the Standard ------------------------------- In cases where default int leads to an ambiguity, it is necessary to write special disambiguating rules. This serves to complicate the Standard and has the potential to leave holes where no one has noticed that an ambiguity might exist. Deprecation or banning leaves no doubts that such ambiguities will never be resolved in favour of default int. In particular, default int makes specifying the syntax of C++ difficult and particularly sensitive to context. d) Plain bad style ------------------ Overall, at best defaulting int is viewed as bad style, and, from a software engineering viewpoint, detrimental to the quality of software. No functional advantage is obtained from default int. The reduction in typing required is not significant for modern computing systems. 3) Why we have default int -------------------------- Default int is in the Working Paper because it was inherited from C. (C++ has inherited the Classic C syntax, I'm told). The ISO C Standard provides the following syntax: declaration: declaration-specifiers init-declaratior-list[opt] ; declaration-specifiers: storage-class-specifier declaration-specifiers[opt] type-specifier declaration-specifiers[opt] type-qualifier declaration-specifiers[opt] init-declarator-list: init-declarator init-declarator-list, init-declarator init-declarator: declarator declarator = initialiser Note that typedef is a storage-class-specifier. In a declaration of the form static const int // declaration specifier *x, y(void); // init-declarator-list the declaration specifier need not contain a type-specifier, storage-class-specifier, or type-qualifier, although one of these is required: the type-specifier defaults to int if omitted. This means that the following declarations are allowed by the syntax: static *x, y(void); const *x, y(void); extern x; There is a specific constraint requiring at least a declarator, a tag, or the members of an enumeration so that const ; // no declarator static struct { int x; }; // no tag are not declarations, whereas enum {one=1}; // members of an enumeration struct atag { int x; }; // has a tag are. The type specifiers include syntax for a struct-or-union-specifier which depends on a struct-declaration, which is one of the component declarations of a struct or union. That contains the syntax struct-declaration: specifier-qualifier-list struct-declarator-list ; specifier-qualifier-list: type-specifier specifier-qualifier-list type-qualifier specifier-qualifier-list which indicates that either a type-specifier or a type-qualifier is mandatory: struct X { int x; // ok, type-specifier provided const y; // ok, type-qualifier provided y; // not allowed }; For function parameters, there is syntax parameter-declaration: declaration-specifiers declarator declaration-specifiers abstract-declarator[opt] abstract-declarator: pointer direct-abstract-declarator pointer: * type-qualifier-list[opt] * type-qualifier-list[opt] direct-abstract-declarator: (abstract-declarator) direct-abstract-declarator[opt] [constant-expression] direct-abstract-declarator[opt] (parameter-type-list[opt]) An explicit constraint allows only register as a storage-class-specifier. Effectively, one can write: void f( const *(*)() ); // means void f( const int *(*)() ); void f( register x); // means void f( register int x); 4) Cases where we might want to continue to allow it ---------------------------------------------------- While it would be nice to get rid of default int altogether, there are two situations where it is useful to preserve it: 1) extern "C" wrapped around C function declarations included with a #include 2) complilation of C code as C++ code 3) existing C++ code that uses default int In these cases, a large amount of work might be required to convert code, and a straight ban on default int might be considered unacceptable. In particular, some difficulties may result if a large body of stable C code is used with C++, and any modification of the code is considered dangerous. In addition, there may be some C++ code that uses default int. To some extent, I believe breaking this code if it utilises default int is actually desirable and may improve the quality of the code. 5) Ban or Deprecate? -------------------- The alternative to a ban on default int is to deprecate it, that is, to give advance notice that the next version of the Standard will not allow it. I proposed a compromise between these two positions, which bans some uses of default int immediately, and deprecates the other uses. I do so with some reluctance, however: I personally would be quite happy with an outright ban on all uses of default int, even in extern "C" code. The rationale for the distinction is: a) deprecation is allowed where use of default int is common and the code resembles (or is) C b) default int is banned in clearly C++ only contexts However, wherever there is syntax with an ambiguous interpretation, which can be resolved by assuming that there is no default int, then the resolution should be as if int were not defaulted. The resolution I have recommended is that: a) the decl-specifier-seq is not optional in a declaration: one of the keywords 'extern' 'auto' 'static' 'register' 'typedef' 'inline' 'virtual' 'friend' 'template' 'const' 'volatile' 'mutable' 'struct' 'union' 'enum' or 'class' or the name of an object type, or a typedef name, must be the first word of a declaration. b) the type-specifier may not be elided from a decl-specifier-seq if it contains the words 'typedef' 'inline' 'friend' 'mutable' 'virtual' or 'template' c) the type-specifier may not be elided if a pointer to member is declared d) the type-specifier may not be elided anywhere in the scope of a template declaration or definition e) eliding the type-specifier in other cases is deprecated by specifying the syntax as an anachronism. Thus, uses such as const x = 1; extern x; auto x; static x; register x; are deprecated rather than banned. f) except that no type specifier may be elided anywhere in the scope of a template declaration or definition