Deprecation of the static keyword... no more?
Asked Answered
P

3

104

In C++ it is possible to use the static keyword within a translation unit to affect the visibility of a symbol (either variable or function declaration).

In n3092, this was deprecated:

Annex D.2 [depr.static]
The use of the static keyword is deprecated when declaring objects in namespace scope (see 3.3.6).

In n3225, this has been removed.

The only article I could find is somewhat informal.

It does underline though, that for compatibility with C (and the ability to compile C-programs as C++) the deprecation is annoying. However, compiling a C program directly as C++ can be a frustrating experience already, so I am unsure if it warrants consideration.

Does anyone know why it was changed ?

Pinole answered 18/1, 2011 at 16:41 Comment(9)
You declare objects at namespace scope in C?Rawlins
heh, thx, found where to get hold of it at. Tried to delete comment but you beat me there.Libbylibeccio
Question arose from stackoverflow.com/questions/4725204/…Tankoos
This also gives the C++ Committee the opportunity to unundeprecate something in the next version of the Standard :-)Rearrange
stackoverflow.com/questions/4422507/…Kerril
@MatthieuM. - sorry, I just thought that that question should be linked in here (it wasn't afaict) and so I just dropped the link into the commentsKerril
@MartinBa: Ah! Okay, I thought you wanted to ask some question and hit the Enter key too fast somehow but was surprised not to see the question coming after one hour :)Pinole
possible duplicate of Why are anonymous namespaces not a sufficient replacement for namespace-static, according to the standards committee?Sihun
@MatthieuM.: In above post, there're objective answers to your question, which tells where all static works when unnamed namespaces don't; the answers here seem primarily opinon based.Sihun
R
83

In C++ Standard Core Language Defect Reports and Accepted Issues, Revision 94 under 1012. Undeprecating static they note:

Although 7.3.1.1 [namespace.unnamed] states that the use of the static keyword for declaring variables in namespace scope is deprecated because the unnamed namespace provides a superior alternative, it is unlikely that the feature will be removed at any point in the foreseeable future.

Basically this is saying that the deprecation of static doesn't really make sense. It won't ever be removed from C++. It's still useful because you don't need the boilerplate code you would need with unnamed namespace's if you just want to declare a function or object with internal linkage.

Reverent answered 18/1, 2011 at 16:54 Comment(10)
Thanks for the link, didn't thought of consulting the defects :/Pinole
Well, it seems deprecation would encourage people to use unnamed namespaces instead, which would be a good thing.Chirr
@unaperson: If for no other reason, then because unnamed namespaces provides the same mechanism for making variables, constants, functions, and types internal to their TU. static class ... , OTOH, won't work.Chirr
@nbt: Because you can't use static symbols as template arguments and because many newbies would find static easier to use and then are untempted to try out <functional> and <algorithm> et al. Just a quick thought.Nodababus
" because you don't need the boilerplate code you need with unnamed namespaces"? What "boilerplate code"? Something beyond "namespace {" and "}"?Estuary
@Estuary compared to static there are two additional braces and on many coding styles you will be writing at least 3 lines of code to indent the declaration.Reverent
i like to declare helper functions, globals, etc static and as close as possible to their usage. the namespace { } syntax is ugly for this purpose, and "static" is fine. other people like to put all their helper stuff at the top of a file. the namespace {} syntax is more useful there. so it's really a matter of style. they are both, ultimately, equivalent.Loft
@phresnel no such thing as a "static" class/typdef. local classes in a file aren't available outside that file. so no need for namespace{} around them. only functions (which can always be static) & global - which don't conflict with templates.Loft
Not to mention that they only talk about objects in the removed part, which was silly in itself. What about functions? The removal has my whole hearted approval lol.Ensample
@ErikAronesty If you have a "local class" in another file with the same name then you will commit an ODR violation.Breakfast
I
36

I will try to answer your question, although it is an old question, and it does not look very important (it really is not very important in itself), and it has received quite good answers already. The reason I want to answer it is that it relates to fundamental issues of standard evolution and language design when the language is based on an existing language: when should language features be deprecated, removed, or changed in incompatible ways?

In C++ it is possible to use the static keyword within a translation unit to affect the visibility of a symbol (either variable or function declaration).

The linkage actually.

In n3092, this was deprecated:

Deprecation indicates:

  • The intent to remove some feature in the future; this does not mean that deprecated features will be removed in the next standard revision, or that they must be removed "soon", or at all. And non-deprecated features may be removed in the next standard revision.
  • A formal attempt to discourage its use.

The latter point is important. Although there is never a formal promise that your program won't be broken, sometimes silently, by the next standard, the committee should try to avoid breaking "reasonable" code. Deprecation should tell programmers that it is unreasonable to depend on some feature.

It does underline though, that for compatibility with C (and the ability to compile C-programs as C++) the deprecation is annoying. However, compiling a C program directly as C++ can be a frustrating experience already, so I am unsure if it warrants consideration.

It is very important to preserve a C/C++ common subset, especially for header files. Of course, static global declarations are declarations of symbol with internal linkage and this not very useful in a header file.

But the issue is never just compatibility with C, it's compatibility with existing C++: there are tons of existing valid C++ programs that use static global declarations. This code is not just formally legal, it is sound, as it uses a well-defined language feature the way it is intended to be used.

Just because there is now a "better way" (according to some) to do something does not make the programs written the old way "bad" or "unreasonable". The ability of using the static keyword on declarations of objects and functions at global scope is well understood in both C and C++ communities, and most often used correctly.

In a similar vein, I am not going to change C-style casts to double to static_cast<double> just because "C-style casts are bad", as static_cast<double> adds zero information and zero safety.

The idea that whenever a new way to do something is invented, all programmers would rush to rewrite their existing well-defined working code is just crazy. If you want to remove all the inherited C ugliness and problems, you don't change C++, you invent a new programming language. Half-removing one use of static hardly makes C++ less C-ugly.

Code changes need a justification, and "old is bad" is never a justification for code changes.

Breaking language changes need a very strong justification. Making the language very slightly simpler is never a justification for a breaking change.

The reasons given why static is bad are just remarkably weak, and it isn't even clear why not both objects and function declarations are deprecated together - giving them different treatment hardly makes C++ simpler or more orthogonal.

So, really, it is a sad story. Not because of the practical consequences it had: it had exactly zero practical consequences. But because it shows a clear lack of common sense from the ISO committee.

Ironwood answered 26/11, 2011 at 5:18 Comment(25)
As you yourself points out, the point of deprecating it is to discourage it's use. Yet you make no argument that discouraging its use is wrong. I certainly hope that nobody's out there encouraging people to use namespace-scoped static declarations over anonymous namespaces. Not unless they specifically need to cross-compile C.Catcall
I don't care that much about people using global scope static or anonymous namespaces, I am not encouraging or discouraging either. My point is that if you really want to discourage people to use anonymous namespaces you have to give them good argument. In practice, I believe that in most implementations entities declared in an unnamed namespace are symbols exported with a random name, thus growing the export table. Entities declared as static, OTOH, are not exported in any way. Thus many people choose, based on that observation, to use static.Ironwood
"As you yourself points out, the point of deprecating it is to discourage it's use." The point of discouraging its use is that it might disappear some day. My point is that namespace-scope static won't disappear ever, so it's wrong to deprecate it. "Yet you make no argument that discouraging its use is wrong." I have seen no convincing argument that shows that use of namespace-scope static is "wrong". Deprecating it just to discourage its use is wrong, because no one actually believes it's going to disappear, and because it doesn't convince people that using it is "wrong".Ironwood
"I certainly hope that nobody's out there encouraging people to use namespace-scoped static declarations over anonymous namespaces." Some people think that static is better, and encourage its use over anonymous namespaces. My point isn't that either one is better than the other; my point is 1) that no feature (static, anon namespaces) is "wrong". 2) that deprecating a useful, well-defined, widely used feature that isn't going to go way - ever - is wrong. Doing that lowers the credibility of the C++ committee (and it is already quite low).Ironwood
The whole language will "disappear some day". Let's deprecate C++.Verbenaceous
@Non-StopTimeTravel Like Fortran did? (when?) You should know, as a time traveler. And COBOL?Ironwood
@curiousguy: They haven't yet, which proves precisely nothing!Verbenaceous
@NicolBolas it isn't just me: the CWG (core working group) voted "Anonymous namespaces are not a sufficient replacement for the functionality." see n3296Ironwood
"In a similar vein, I am not going to change C-style casts to double to static_cast<double> just because "C-style casts are bad", as static_cast<double> adds zero information and zero safety." My eternal fight with many software engineers that keep complaining about my libertinous use of C style casts from one primitive to the other.Bors
@Bors If you use this as your argumentation you will probably cause some more fights, as it is obviously wrong: static_cast adds information, namely that this cast is done at compile time, while allowing at least some error checking. The reasons to avoid C-style casts are not so inane as "they are bad". Error checking may not always be required, but it is harmless and more obvious to other coders to use static_cast.Alvey
That's irrelevant for primitives. Primitve casting is ALWAYS a safe operation, there's nothing a static cast can do for you when primitve casting, there's no checks to do about whether or not you can cas a flaot to an int and vice versa.Bors
@Alvey "namely that this cast is done at compile time" as opposed to what? what is "cast done at run time"? "while allowing at least some error checking" which checks, exactly?Ironwood
@Bors true, but it's not true that a static cast adds zero information or safety, because it's always more specific than a C-style cast, which does not provide any safety. @curiousguy: dynamic_cast, as you cannot statically downcast a pointer/reference to a polymorphic type if you don't know what its dynamic type will be. An example of a legal C-style cast that is not a legal static_cast is char c = 0; auto *p = (int*) &c;. This would require the "here be dragons" reinterpret_cast, which also makes abundantly clear to others that you are throwing type safety out of the window.Alvey
"which does not provide any safety" Neither does a static cast, when we talk about primitives. There exists 0 differences between (int)float and static_cast<int>(float) aside from the length of writing. There's no information to be added to that operationBors
@Alvey Yes and when was a C style ptr cast interpreted as a dynamic_cast? Never obviously. And when was dynamic_cast related in any way w/ casting numbers like integers and fp? Never, again. So while your point about "new style" (they aren't "new" anymore) casts being more explicit and more readable (by making stuff "abundantly clear to others") is well received, they apply mostly to casts involving ptr at some point. They don't apply in any way to a cast to a double. You can't just use an argument about ptr casts and "transpose" it to some other type.Ironwood
@Makogan: I agree if it's some local variable you control, but suppose the variable is later changed to a pointer: the static cast is now illegal but the C cast will continue to compile. Unlikely, but slightly less safe. curiousguy: Indeed never, yet it just allows too many things too quietly. My main point is that C++ casts show potentially helpful information not only to the compiler, but also to other readers/maintainers of your code, which the C syntax can't express. That said, you are entirely right that (double) casting is quite benign and I won't castigate you for doing it.Alvey
At the point where you are casting integer values to pointers and vice versa you are likely doing hardware stuff, i which case you should NOT use C++ casts as they will be very confused about everything. In fact, that's a scenario where you throw all of C++ out the window and do plain CBors
@Bors What can you do in C that you can't do in C++?Ironwood
Nothing, but C isn;t name mangled and doesn't start raising a bunch of warnings and compilation errors when you do extraneous things like magically typecasting integers to pointers or touching memory you are "not supposed to touch". Which is 90% of embedded developmentBors
@Bors You can avoid name mangling with the huuuge effort of prefixing your function declaration with extern "C" You can cast pointers to integers and back in C++ just like you can in C. I have no idea which warning about "touching memory you are "not supposed to touch"." you are talking about.Ironwood
@Bors You can use the C++ specific syntax (reinterpret_cast) to perform these casts. Writing C interfaces in C++ is not writing C code. You can use all C++ feature in that code. Also, flagged.Ironwood
There was no disagreement except at the end when you said all C++ features are available in an extern C block. The entire point of discussion is if C++ style casts serve any purpose when casting primitives, they don't.Bors
Let us continue this discussion in chat.Ironwood
static_cast<double> adds zero information and zero safety - from the view of the language parser it sure adds zero information. From the view of Ctrl-F in your editor, or the view of grep, it adds a way of locating all casts for audits/reviews. But given the availability of static code analysis frameworks such as libclang that make it easy to locate any such construct, the admonition against such C-style casts is indeed getting long in the tooth.Shawnshawna
@Kubahasn'tforgottenMonica "the view of Ctrl-F" That could be an argument in O'Caml or other languages where all type conversions are explicit. With all the implicit conversions in C++, there is no chance to find all instances. Also I simply don't believe an int to double conversion is a such an important or dangerous operation that you need to tag it.Ironwood
O
16

Deprecated or not, removing this language feature would break existing codes and annoy people.

The whole static deprecation thing was just wishful thinking along the lines of "anonymous namespaces are better than static" and "references are better pointers". Lol.

Obvert answered 18/1, 2011 at 16:55 Comment(10)
"References are better pointers"? No, smart pointers are smarter pointers. You can't use references for memory allocated from the heap, err, free store.Vessel
Sorry, I forgot to end it with an ironic smiley.Obvert
@Dan: That's exactly what this answer says: "wishful thinking" along a similar faulty line of thought. Unnamed namespaces are an important feature, just as global-scope-static is, though for slightly different reasons, and even though they have some overlap in applicability.Tankoos
@Fred, @Maxim: Sorry if I misunderstood, or if my memory's faulty. But I don't categorize "references are better pointers" as being equivalent to "anonymous namespaces are better than static" as a case of wishful thinking. I'm well aware of the attempt at making the latter stick, but I don't remember anyone making a serious proposal to replace pointers with references. Again, maybe it's my own awareness that's lacking.Vessel
@Dan: I do occasionally see people on SO and elsewhere that try to push "references are better pointers", but it of course didn't have the serious effort to remove it from the language that the other did. I think you simply misunderstood, because I can't see that Maxim was ever espousing it himself. *shrug*Tankoos
@Fred Nurk, @Maxim: OK, that's (at least one place) where we've disconnected: I didn't mean to suggest that Maxim had made that claim himself. The quotes in his post made it clear to me that he was quoting some third party. All I intended was to question whether any significant third party had ever made a serious claim that "references are better pointers", because I don't see how anyone could even begin to make that case. (I guess I've missed such discussions when they've happened here.) Perhaps I should have said "One can't use references..." instead of "You can't use references..."Vessel
@DanBreslau: You can't use references for memory allocated from the heap, err, free store Why's that then?Verbenaceous
@Lightness Races in Orbit: That is, you can't do something like char &foo = new char;, and you certainly can't delete foo; if foo is a reference.Vessel
@DanBreslau: char* foo = new char; char& ref = *foo; Just because you are given a pointer initially says nothing whatsoever about your ability to use references.Verbenaceous
The point (so to speak :-) is that a reference is always dependent on another variable; you can't replace a pointer with a reference variable. (Actually, I suspect you could, but it would require some incredibly ugly hacks that are probably implementation-dependent.)Vessel

© 2022 - 2025 — McMap. All rights reserved.