Visual C++ Compiler Warning C4932

Message Text

__identifier(identifier) and __identifier(identifier) are indistinguishable

Severity

This is a level 4 warning.

Circumstances

Many keywords, as tokens, are permitted multiple names as text. By far the most typical, but by no means the only, examples are keywords that may be given with either two leading underscores or just one. The multiple names tokenise indistinguishably. They all map to the same token, which has a standard name (typically, but again not necessarily, the name with two leading underscores). When different names for the same keyword are given to __identifier, the identifier tokens that are created each have the same name, i.e., the standard name of the keyword.

For example, if /Ze is active, the keyword token whose standard name is __cdecl is permitted the other names _cdecl and cdecl. Feed any of these to __identifier and the identifier that is created is named __cdecl.

Warning C4932 seems intended to alert that the identifier actually created by __identifier may have a name other than what is given as the argument, or that the same identifier can be produced from other arguments. The two identifier placeholders in the message text are examples of arguments that would produce the same identifier.

Thus, for the preceding example, compiling

extern int __identifier (cdecl);        // C4932, if /Ze

with /Ze produces warning C4932 both to tell the programmer that the identifier is not named cdecl and to help with understanding the compiler’s complaints about redefinition should there elsewhere be something like

void __identifier (__cdecl) (void);     // C4932 and C2365, if /Ze

That said, the usefulness of warning C4932 is somewhat diminished because, as actually implemented, it does not cover the possibilities comprehensively. For warning C4932 to occur, the keyword that is given as the argument to __identifier must have among its permitted names one that begins with two underscores and another that is this same name but with only one leading underscore. The existence of this one-underscore form requires in practice that the /Ze or -ZE option be active. The keyword as actually given to __identifier may be the form with two leading underscores, or with one, or it may be another form altogether. Except in one case that is surely an oversight (see below), the first and second identifier placeholders in the message text are respectively the forms with two leading underscores and one.

(All the disabled keywords, and also __compileBreak and __feacpBreak, are excluded from the present discussion on the grounds that they cannot actually be given as arguments to __identifier. Though the may be placed as text where __identifier expects an argument, the corresponding tokens are discarded from the token stream and never contribute to the __identifier syntax.)

Applicable Keywords

In the general case for causing warning C4932, the keyword has its standard name formed as two underscores followed by some base name, and has at most the one alternative form, being one underscore and the same base, which is a keyword only when the /Ze or -ZE option is active.

Thus, under the /Ze or -ZE options, giving either __basename or _basename to __identifier produces an identifier named __basename, with a warning. The message text is a straightforward alert to these two forms that produce the same identifier. The two identifier placeholders in the message text are __basename and _basename respectively. With neither option active, _basename is not a keyword, __identifier produces __basename from __basename and _basename from _basename, and there is nothing to warn about.

The applicable keywords (in their standard forms with two leading underscores) for this general case are:

__asm, __assume, __based, __declspec, __except, __fastcall, __finally, __forceinline, __int16, __int32, __int64 (if /ZB ≥ 64), __int8, __leave, __multiple_inheritance, __novtordisp, __pragma, __ptr32, __ptr64, __single_inheritance, __stdcall, __thiscall (if -Binl), __try, __uuidof, __virtual_inheritance and __w64

Special cases are presented by three other keywords.

When /Ze or -ZE is active, giving any of __alignof, _alignof, __builtin_alignof and _builtin_alignof to __identifier produces an identifier named __builtin_alignof, with a warning. The message text refers only to __builtin_alignof and _builtin_alignof, no matter which of the four possibilities was actually given, but this is only a cosmetic problem.

With /Za and without -ZE, the single-underscore forms _alignof and _builtin_alignof are no longer keywords and present __identifier with no trouble. However, the two double-underscore forms, __alignof and __builtin_alignof, still both convert to the one identifier, __builtin_alignof. Yet there is no warning.

The __cdecl keyword differs from the general case only by having a third form, cdecl, but since it is a keyword only when _cdecl is also a keyword, the only problem it presents is again cosmetic. If /Ze is active, giving cdecl as the argument to __identifier produces an identifier named __cdecl, with a warning, as expected, but the message text may frustrate since it cites __cdecl and _cdecl, rather than what was actually given.

Like __builtin_alignof, the inline keyword always permits at least two forms, even when the form with one underscore is not a keyword. With /Za and without -ZE, both inline and __inline are keywords, and both convert to the same identifier inline, yet there is no warning.

Note also that inline is unusual in that its standard form is the one with no underscores. When there is a warning, i.e., when /Ze or -ZE is active and __inline, _inline and inline all convert to the same identifier, the code for formatting the message text assumes that the standard form has two leading underscores and generates the second identifier placeholder by dropping a character from the first, so that the hapless programmer who sees the warning can wonder what the compiler means by

__identifier(inline) and __identifier(nline) are indistinguishable

Documentation Status

For who can imagine what reason, the product documentation cites only __finally and __try as applicable keywords. This is especially odd, since the documentation elsewhere (C++ Keywords) states explicitly that these keywords are not supported in single-underscore versions.