When to use “::” for global scope in C++?

2019-04-18 17:32发布

问题:

Every once in a while, I stumble across some code that I'm maintaining that challenges the way I think about my code style. Today was one of those days...

I'm aware that about why you would want to use the scope operator to define global scope. In fact, here scope resolution operator without a scope is a great link tell you why.

However, I saw something that made me think today. All the classes in question were wrapped into a namespace for the project (good!), but I did see copious usage of the global scope operator. Namely, it was used for everything from C libraries (with the exception of uint8_t and the like... yes, the programmer used the .h version of this library since apparently the version of g++ they were running still threw warnings about the new C++ standard). Is this useful? I view this as just as a waste of characters (reminds me of using the this pointer... except in the case of the copy constructor and assignment operator where it helps in clarifying which object is which). Am I missing something? Sure, someone can come around and name something along the lines of usleep() or stderr (where I saw the most usage of "::"), but won't they know that doing so will probably break something horribly? At what point do you say "screw it" in terms of the scope operator and just tell yourself that someone who names a function a certain way within your namespace is asking for trouble?

So my question is... what is the "correct" (subjective I understand) way of using the global scope operator in this context? Should everything not included in std or your own namespaces have the global scope explicitly defined? I tend to err on the side of caution and use "std::" to avoid the using directive, but is anything gained from using the global scope operator here? I tend to think for clarity's sake it does lend to the fact that we aren't getting the variable or function in question from the current namespace, but I'm torn between including it and not given today's developments.

As always, thanks for the help and guidance as I look to make my code cleaner, more readable, and (of course) significantly more awesome.

回答1:

I use it quite infrequently; only when some ambiguity needs resolving for whatever reason. This is pretty subjective, though.

There may be some occasions (say, inside a template) where you're worried about ADL causing ambiguities only in certain cases:

template <typename T>
void foo(T t)
{
   ::bar(t);  // :: just in case there's a `bar` in `T`'s namespace
}


回答2:

There is almost no correct answer to this as it's almost totally style related, with the exception that if you think you may want to change from where you are going to import some declaration(s)/definition(s), in which case, when you use them, you don't specify any scope and import them using the using directive (to import a subset from the namespace) or the using namespace directive to import the entire set from the namespace.

Mostly the using directive is used as a convenience directive, but it is a powerful way to direct which declarations/definitions are used. My preference is to not specify the scope and import the declarations. Doing this allows for easy changes if ever they are needed while reducing visual noise. Also, specifying the scope would mean I'd be "locked in" from where I am getting the declarations (well, I'd have to do a global search and replace to change it).

If ever there is a conflict (you try an use a declared item with the same name that has been imported from more than one namespace) the compiler will let you know, so there's no real danger.



回答3:

Readable code is that has the least amount of noise. namespace prefixes normally provide nothing but noise. So baseline is to not have them at all.

Namespaces were introduced to C++ mainly to handle 3rd party stuff out of one's control. To allow libraries drop prefixing, while clients can use terse names by applying using.

Just because you can have the same name in many namespaces does not imply it is a good idea to too. If a project uses some environment, platform API, library set, whatever that puts name in global, those names are better be avoided for other purposes. As with or without prefixes they will bear mental overhead.

Use of :: is rare in well-shaped code, and frequent uses appear in wrapper classes for that same functionality.



回答4:

Consider the following cases.

Public library.

You are writing an exportable library with public headers. And you absolutely have no clue in what environment your headers will be included. For example, someone may do:

namespace std = my_space::std;
#include "your_header"

And all your definitions will be corrupted, if you simply use: std::list<int>, etc. So, it's a good practice to prepend :: to everything global. This way you can be absolutely sure what you're using. Of course, you can do using (in C++11) or typedef - but it's a wrong way to go in headers.


Collaborative .cc/.cpp files.

Inside your own code that is not exposed to public in any way, but still editable not only by you - it's a good practice to announce, what you're going to use from outside of your namespace. Say, your project allows to use a number of vectors - not only an std::vector. Then, where it's appropriate, you put a using directive:

// some includes

using vector = ::std::vector<int>;  // C++11
typedef ::std::vector<int> vector;  // C++03

namespace my_namespace {
...
}  // namespace my_namespace

It may be put after all includes, or inside specific functions. It's not only gives control over the code, but also makes it readable and shortens expressions.

Another common case - is a mixing of global C functions and a C++ code. It's not a good idea to do any typedefs with function names. In this situation you should prepend C functions with the global scope resolution operator :: - to avoid compilation problems, when someone implements a function with a same signature in the same namespace.

Don't use :: for relative types and namespaces - or you'll lose the benefit of using namespaces at all.


Your own code.

Do what you want and how you want. There is only one good way - the way you fill comfortable with your own code. Really, don't bother about global scope resolution in this situation, if it's not requiered to resolve an ambiguity.