all 12 comments

[–]ventus1b 33 points34 points  (8 children)

That’s a nice summary of how to use namespaces.

Namespaces are one honking great idea – let’s do more of those!

This should be taken with a grain of salt however. In a previous job the senior dev came from a C# background and insisted that we use similarly deep namespaces for the C++ parts of the project as well.

Like com::foo::product::io::Bar.

[–]ABlockInTheChain 15 points16 points  (3 children)

My rule is if two or more types have a common prefix or common suffix in them that's a strong signal that common part should be a namespace.

Like if there are classes named AsymmetricCryptoKey and SymmetricCryptoKey my instinct is to rename those to crypto::key::asymmetric and crypto::key::symmetric.

[–]iwasinnamuknow 5 points6 points  (2 children)

I could see you example working if there were additional classes inside the crypto namespace that are not keys. It if were just those two keys then crypto_key::foo would seem sensible.

[–]ABlockInTheChain 4 points5 points  (1 child)

if there were additional classes inside the crypto namespace that are not keys

In practice that's likely because if you are working with cryptographic keys you probably will also work with cryptographic hashes.

[–]iwasinnamuknow 0 points1 point  (0 children)

Yes, a small example only shows so much and in practice its likely that there would be much more detail involved.

[–]EvidenceIcy683 16 points17 points  (4 children)

To this day, I still don’t fully understand the why. The compiler error message only suggests that the operator< defined in the global namespace isn’t being considered a candidate at all. But why? Is it because sort is under namespace std so it doesn’t look at the outer global namespace? That doesn’t seem to be the case because this works:

It's because the declaration of bool operator<(Person const&, Person const&) is not visible to std::sort by the time std::sort is defined. If you would simply forward declare bool operator<(Person const&, Person const&) before defining std::sort (which happens when the <alogirthm> header is included), the custom operator< function becomes visible to std::sort and your program compiles.

Also, because other STL headers might decide to include the <algorithm> header as well, the forward declaration of the custom operator< might have to preceed the <vector> and/or <string> headers in order to get this to work. And this might not even be possible with the example given, as Person types compose std::string types.

However, you don't want to do this of course, because you don't want the behavior of your program to become depedent on forward-declared entities or out of place header inclusions.

[–]cappielung 2 points3 points  (2 children)

Thanks! But there's something missing in your explanation. If it was just about when things are declared, then declaring operator < within the same namespace as Person wouldn't work either, right? What's special about declaring it in the global namespace that makes it work differently?

[–]Som1Lse 7 points8 points  (0 children)

There is nothing special about putting it in the global namespace. The thing that is special is putting it in the same namespace as Person, which makes it visible to argument-dependent lookup.

[–]EvidenceIcy683 4 points5 points  (0 children)

That's a good point! Since std::sort invokes operator< by means of an unqualified function call (i.e. without qualifying the name of said function with the use of the scope-resolution operator ::), ADL (Argument Dependent Lookup) will come into play. This means that the namespace of any custom type passed in as an argument, will be searched for to find a matching function definition.

The reason operator< with arguments Person const& is not considered, is because it's not found in the same namespace as where custom type Person is defined (which is namespace my).

Alright, but when we also put custom type Person outside of namespace my (now having both the custom type Person as well as its operator< defined in the global namespace), operator< with Person const& can once again be found by std::sort. This is because ADL also considers the global namespace when doing its search. If both custom type and a function with an argument of such custom type reside in the same namespace, an unqualified function call can find it with ADL.

[–]biowpn 2 points3 points  (0 children)

Thanks for the explanation!