C++ Weekly - Ep 305 - Stop Using `using namespace`
HTML-код
- Опубликовано: 4 сен 2024
- ☟☟ Awesome T-Shirts! Sponsors! Books! ☟☟
Upcoming Workshop: C++ Best Practices, NDC TechTown, Sept 9-10, 2024
► ndctechtown.co...
Upcoming Workshop: Applied constexpr: The Power of Compile-Time Resources, C++ Under The Sea, October 10, 2024
► cppunderthesea...
T-SHIRTS AVAILABLE!
► The best C++ T-Shirts anywhere! my-store-d16a2...
WANT MORE JASON?
► My Training Classes: emptycrate.com/...
► Follow me on twitter: / lefticus
SUPPORT THE CHANNEL
► Patreon: / lefticus
► Github Sponsors: github.com/spo...
► Paypal Donation: www.paypal.com...
GET INVOLVED
► Video Idea List: github.com/lef...
JASON'S BOOKS
► C++23 Best Practices
Leanpub Ebook: leanpub.com/cp...
► C++ Best Practices
Amazon Paperback: amzn.to/3wpAU3Z
Leanpub Ebook: leanpub.com/cp...
JASON'S PUZZLE BOOKS
► Object Lifetime Puzzlers Book 1
Amazon Paperback: amzn.to/3g6Ervj
Leanpub Ebook: leanpub.com/ob...
► Object Lifetime Puzzlers Book 2
Amazon Paperback: amzn.to/3whdUDU
Leanpub Ebook: leanpub.com/ob...
► Object Lifetime Puzzlers Book 3
Leanpub Ebook: leanpub.com/ob...
► Copy and Reference Puzzlers Book 1
Amazon Paperback: amzn.to/3g7ZVb9
Leanpub Ebook: leanpub.com/co...
► Copy and Reference Puzzlers Book 2
Amazon Paperback: amzn.to/3X1LOIx
Leanpub Ebook: leanpub.com/co...
► Copy and Reference Puzzlers Book 3
Leanpub Ebook: leanpub.com/co...
► OpCode Puzzlers Book 1
Amazon Paperback: amzn.to/3KCNJg6
Leanpub Ebook: leanpub.com/op...
RECOMMENDED BOOKS
► Bjarne Stroustrup's A Tour of C++ (now with C++20/23!): amzn.to/3X4Wypr
AWESOME PROJECTS
► The C++ Starter Project - Gets you started with Best Practices Quickly - github.com/cpp...
► C++ Best Practices Forkable Coding Standards - github.com/cpp...
O'Reilly VIDEOS
► Inheritance and Polymorphism in C++ - www.oreilly.co...
► Learning C++ Best Practices - www.oreilly.co...
References:
isocpp faq - isocpp.org/wik...
Abseil Tip of the Week - abseil.io/tips...
It happens to me every semester that 1. I assign an exercise of writing a "swap" routine 2. the student uses "using namespace" and 3. they manage to call the "std::swap" rather than their own.
ADL is bad in general. Unfortunately namespace injection is forbidden by the standard to the std. The correct way would be to inject a new swap inside std, and call std::swap. Because of ADL, swap and many other identifiers have become keywords. People program in a minefield thanks to ADL.
And do it once and learned their lesson….. bug is the compiler / linker not warning about the naming conflict. But that is c++, making easy things bloated and hard things even harder….
When mixing math related libraries, you will never try to use "using namespace"
/edit: You forgot to mention what could be used instead: namespace aliasing. E.g., this causes no conflicts when used in cpp files:
namespace s = std; namespace c = company2;
s::vector s;
float c = c::calculate(2);
Jason, I agree with the point you are making here, and I heard the point you made about overload resolution at around 10:45. As I understand it, the precise issue that you are highlighting is that "using namespace" will merge together entirely separate pieces of code into the same scope for argument dependent lookup/overload resolution which can have unexpected results due to implicit conversion. This definitely supports the argument to avoid using 'using namespace' except in the cases you gave later.
However, based on some of the comments below, I think it might have helped to explicitly show that if the two functions signatures had been identical, the compiler _would_ have warned. And also that if you had put both (int and double) functions in the same namespace, that the implicit conversion combined with overload resolution would still have occurred (and chosen the int version if your argument is an int, but also would have switched to the double version if your argument had been a double - 2.0) regardless of 'using namespace' directives.
All this warnings are well justified but to be honest, if you are writing tests for your code, you can feel safe. Like for the example above, a simple unit test for emptycrate::calculate() guarantees you that in case your function will be somewhere substituted, this will be detected before it goes outside your dev machine.
1 test does not prove that all uses of your function, with all associated #includes, and all potential "using namespace" declarations are considered.
It seems safe to do `std::literals` even at a broad scope (though still not header files) because conforming code will never use the same suffix as the standard library, even in later C++ standards (the naming conventions are different and non-overlapping).
Except for the fact that the standard library uses s for both "seconds" and "string" so...
@@cppweekly It should not conflict, the `s` for string applies to string literals only and the `s` for seconds applies to numeric literals only
A concrete example: `123s` would always generate std::chrono::seconds and `"123"s` would always generate a std::string.
we like the advice - we'd change what you said about the literals to a more specific using - if you are using the string literals stuff, then `using namespace std::literals::string_literals;` is preferred in our code bases as it narrows the scope of the UDL declarations being bought into scope.
Nice
For real, what it miss in C++ is nested using. For example :
using std::{vector, string, string_view, cout};
A bit like Rust does
Exactly. It's like the difference in Python between:
from somemodule import *
and
from somemodule import foo, bar, baz
In order to be useful, I need to be able to _be_ explicit, but not verbose. Unfortunately, with all the gains modern C++ has made, it frequently fails the verbosity test.
@@bloodgain I agree that many of the C++ features fail the verbosity tests. Which, i think at least isn't something that will change any time soon. There are so many features that you have to be verbose to even be able to parse the code. So that problem will only really be fixable by deprecating some of the not so useful/dangerous to use, but that will break backwords compatibility, the worst thing according to many people.
"how am i going to use that one library that isn't being updated since 1995 in my projects now????" maybe we have to let go of some stuff at some point. and the argument that old projects have to be compatible forever is problematic in its own way. Old code doesn't take advantage of new hardware features so it will be slower than it could be and if you only have the library as a binary, you have no idea how it's doing what its doing.
So deprecation could mean innovation in more than just c++, in my mind at least.
I might have gone a bit of topic but i fully agree that Rust and python make a way, way better job.
But when you upgrade a dependency and it is marked as having breaking changes, I guess you expect those kind of changes in behaviour, aren’t you? Otherwise the author didn’t communicate the updates very well.
It gets hyper fun when you're on a large project using unity builds. Now you're spooging namespaces into other files!! 🎉🎉🎉🎉🎉🎉
Oh, great point! I never thought to mention how .cpp files, using namespace, and unity builds can come together for an exciting combination.
Is unity builds a way of compiling a big cpp file even if only a small part of it changed? 🤔
@@enricomariadeangelis2130 No, it's combining cpp files together into one in an effort to reduce build times.
IMO using `using namespace` in my implementation files and with my namespace is fine. Although i don't use any third-party namespaces, especially std.
Can't wait for "stop using c++"
What about using type-aliases/using-declerations in header files, to shorten names?
Given a library with different modules, should there be any reservations on doing something like below:
// library
// A.h. - header with a public interface
namespace lib::moduleA
{
class Object
{
private:
using objB = lib::moduleB::Object; // private type-alias
public:
// unqualified use of ObjB type alias, in public interface
void func1( objB &, …)
void func1( objB &, …)
}
}
I.e. create a private type-alias inside the class, and use that alias for function interface.
This is why I was very careful to say "stop using `using namespace`". You are clearly *not* using `using namespace` in your example.
An important use for 'using namespace' IS in slides, to make examples fit the screen. 😄 People typically assume that the audience is informed enough to understand that it's not an endorsement for using it in actual code.
This is a terrible assumption to make. I strive to have all of my slides represent as much best practices as I can, unless the point is to show bad code.
I'm surprised there isn't a compiler warning flag for overloads from disparate namespaces.
It wouldn't be useful, sometimes you intentionally need that use case. Which I though I demonstrated in the video...?
Excellent video. That really convinced me!
This is is brilliant explanation. Thank you.
I might be wrong, but from the little I know about programming languages, this seems to be a problem inherent to C++. Is it because in C++, nearly everything exposed by the stdlib is exposed in the std namespace directly ? For example, in many other languages (Java, C#, Rust, Python for example), structs/classes, consts and functions of the stdlib are separated into many packages/modules/namespaces, which forces you to be quite specific about what you want to bring in the current scope. To me it feels like there is a trade between repeating the std:: prefix before any stdlib item, and having a long list of imports to bring specific items into the current scope.
Yes, I highly agree! The danger of using namespace std is not because it's bad inherently, it's bad because the top-level std namespace contains so much crap that you can't even reason about it.
Now, doing something like 'use std::io::*' in Rust is still often not great unless you're publicly exporting it, I still wouldn't call it dangerous because it's much more limited
No it's not a stdlib problem as it can show up with any libraries, as demonstrated in the video with the emptycrate and company2_v3 namespaces. It's just that beginners are only exposed to the stdlib and pick up the habit of writing using namespace std; all over the place to save a few characters to type
@@benjaminnavarro865 actually, thinking of it now... Rust doesn't have function overloads. So you can't have two functions with the same name in scope at the same time.
That's also part of it I guess
Something no one else mentioned is that it's also a problem specific to C++ because we allow both type-based and arity-based overloading, and few other languages allow that (Java and C# of your examples) and we have ADL, which AFAIK, is unique to C++.
I thought that it's usually just done in books to save page space / make the examples easier to read. As opposed to actually being best practice.
Yeah, but everyone just copies what they see in books because they assume it's good practice.
Something not covered in the video, I have needed to use a header file before that will use `using namespace std;` and then define a struct byte. std::byte exists since C++17 so this header file has prevented me from using modern C++ with it just by abusing `using namespace std;`.
I ran into a similar issue. To get around it I had to define _HAS_STD_BYTE=0
The only `using namespace` statements I use are the ones for literals
Not in global scope but surely we can use it inside big functions which is doing std::function too many times
The same exact problems of overloads exist, so you do this at your own peril. But also: why are your functions so long?
Thanks!
Since global scope was discussed a bit, let me point out that using directives can be VERY unintuitive at function scope.
What does the following do?
#include
#include
namespace foo {
std::string to_string(char c) {
std::cout
I should also clarify that `using std::swap;` is a different case than this because it's an ADL dance and ADL (very deliberately) doesn't play into this example at all. Don't expect those two to work the exact same as each other, but do expect them to work for their respective use cases.
I think we are using a single using-namespace-directive in our codebase - cause the library we use is using a nested namespace with nothing in the outer namespace......
Still not a good idea and we could run into trouble if they ever decided to put something into the outer namespace.
But well - we could also run into trouble in many other cases like a compiler-update.
We are using g++, but we have identifiers ending with '_t' (reserved by GNU).
We have generated identifiers starting with underscore+Uppercase as well as classes with double or even tripple-underscore in their name.....
I just hope we never get name-conflicts for that.
Wish I could use namespace literals in class/struct scope. As I'm making a lot of string_views and such.
10:43 OK, this is too scary. I'm not gonna use using absolutely..
This is probably a good example of why you should use namespaces also.
As with everything, "using namespace" has its benefits, and you're asking for trouble if you use it in the wrong place, just like macros. The key is knowing where. Or where not
Slideware! Nice term ☺️
It seems like the real issue is that no warnings are generated for those dangerous cases. So the lesson should be "Be careful using "using namespace" until we get better tooling"
What about 'using namespace std::placeholders' ? Despite std::bind sometimes being not recommended in favor of lambda functions, I do like its brevity. Is this okay or considered a code smell?
That, and `std::literals` et al, are perfectly appropriate to use in local scopes, but I still wouldn't generally recommend using them in the global scope.
I most of the time use `using namespace project;` in the ::main() which is outside the namesapce project.
But I never use `using namespace std;`.
I strongly disagree. Namespace std is “standard” , so the standard should be available everywhere. Methods and classes from that namespace are treated as or similar to keywords. Not making this available just adds a lot of unnecessary bloat to the code, just decreases the signal/noise ratio.
This only applies to namespace std, not of other namespaces.
Doubtful examples like “I am writing some extraordinary examples” do not apply to the majority of code. Everyday tasks should be easy (lowest snr) and rare things (in a hypothetical company…) may be more bloaty……
It is interesting that the Java recommendations are just the opposite of the C++ recommendations, while both are to solve similar problems….
I'm just curious if you saw the example in the video where `using namespace` can do unexpected things with overload resolution? This is a real bug in real code that is also illustrated in the book Beautiful C++. amzn.to/42sWkdK
@@cppweekly I agree that it is a bug. But not where you identified it. The bug is to name a class similar to a standard class or keyword, and if this cannot be avoided, not even guard these ill named classes with with another namespace. And, of course, while this example is an ultra rare exceptional flaw, this causes the SNR of c++ to increase.
I recently started using using namespace std; in my header files. I have been writing in C++ for about 22 years. And yes, I have really strong arguments.
If your project coding style meets certain requirements (which are easy to figure out) you are absolutely safe to use using namespace std anywhere you want. And the general recommendation must be to follow these requirements rather than not using an important langauge feature which makes your code more readable.
Just make sure you don't accidentally define any of your own functions with names that the standard library might someday add in the future.
What you said is pretty much why I never use the "using" directive to expose a namespace.
A lot of my code scopes "generic" method names within my namespaces, an example of that would be some open() method. The open() call has different meaning depending on whatever namespace it is in. When pulling in and referencing multiple namespaces in the same code, it becomes of critical importance to keep the namespace prefix on the method name.
Anyways that's why I avoid it.
Cheers,
When nested namespaces are too long, I sometimes use a namespace alias. Even in public headers. This might not be super clean, but I see it as a good compromise.
I mostly agree However, I have code at my work that is frequently nested 3 or 4 namespace down. Adding a namespace alias adds the problem of non-standardization i.e., every programmer will use slightly different aliases. That hasn't been done in our code base, but I think that it will worsen code readability.
However, writing the whole code namespace means lines frequently exceed 100-120 character. It is fine if you are working with dual monitor setup, but it is irritating when people are working from laptops or viewing side by side code diffs.
Any solution for that beyond standardizing on namespace aliases for commonly used namespaces?
What about a linter rule? Theres tools that do copy paste detection sounds like one that searches for aliases that define the same thing wouldn’t be far fetched.
I personally tend to just live with this, but you could potentially add aliases inside of the namespaces themselves, so they still don't leak into global and are available to everyone.
Interesting that compiler prevents you from importing two ambiguous symbols but happily imports and overwrites the same symbols if you are `using namespace`. Wouldn't it make more sense if it simply refused to compile anything with `using namespace` in case of ambiguous symbols citing the ambiguous symbols? Because I do not understand what purpose this silent symbol override serves. Of course I am 100 % behind do not ever using `using namespace` and would add that when reading code having fully qualified names helps A LOT in understanding where things are coming from.
I was chocked as well to learn that the compiler happily allows to identical overloads in that case, sounds to me like the comity overlooked something that, once found, never got fixed.
@@leonmunster8972 actually they aren't identical overloads, at around 10:45 Jason points out that one function takes an int and the other a double. This means that the two functions are not identical. When the function call is resolved, the int version matches best and is chosen. If Jason had changed the code so that both functions had the same signature (e.g. both taking an int or both taking a double), the compiler would have given a warning about ambiguous overloads.
The issue is that the 2 was being implicitly converted to a double when the emptycrate version was being used but once the company version was brought into overload resolution scope, it was a better match for 2 as it took an int without needing conversion.
The message here is that if you write ambiguous code (i.e. the code in the main function), then you are risking unexpected behaviour. If you intentionally omit information (i.e. the exact namespace that you are getting your function from), then the compiler can only do what you ask it to do.
I'm dealing right now with a project with a lot of namespace levels (up 6 and more). You can find symbols in each level. This makes it even hard to manage all the symbols. Usually the developers are creating then a lot of aliases, which makes again reading the code not so easy, as there are no rules on how to create the aliases.
Really? 6 or more?? Can i ask what you are working on that requires something like that?
Was about to comment something similar. In my case the code I use was written by people used to do Java before. All namespace are prefixed with ‘com.company.my’ which is already 3 level deep and serves no purpose.
@@leonmunster8972 It is an automotive software platform with lots of components and a lot of different partners involved.
@@WutipongWongsakuldej that sounds horrible. That would be a reason for me to not use that code, but it's probably work related so not your choice
@@leonmunster8972 well it ends on a positive side, I leave the company :D
What's your opinion on using `using namespace std::literals` at global scope or in header files? Since all user defined literals must start with an underscore, including std::literals can never lead to ambiguity or name conflicts.
It's an interesting point, but definitely still makes me uncomfortable.
Here's one reason why:
s as "second" from chrono: en.cppreference.com/w/cpp/chrono/operator%22%22s
s as "string" from string: en.cppreference.com/w/cpp/string/basic_string/operator%22%22s
oops - there are conflicts inside of the C++ standard itself.
Hi, what style guide will recommend that easy to follow like google style guide ?
There's the one I put together, it might be helpful. github.com/cpp-best-practices/cppbestpractices/blob/master/00-Table_of_Contents.md
@@cppweekly
Thank you for your time
How does using namespace interoperate with modules?
I really have no idea, sorry! I'm waiting for more tool support before I get into modules personally.
It's safe to using namespace in modules, unlike headers, as it won't leak out unless exported.
If I recall in D&E, using namespace was only even added to make upgrading pre C++ 98 code easier.
But the question was/is explicitly about std:: and not somecompany:: namespace.
so typical of C++ - it's features always come with caveats and controversy - "well you really best not use this". The weight of all this is utterly suffocating
Yeah it's really bad in C++. There are so many cases where the comity thought a feature was great and in the end it's worse than what was there before. All the usefulness ripped out to be compatible with the already existing C++ standards.
And all of these somewhat broken systems will be in there until the day C++ is stopped being used (so probably not within my lifetime and i'm not even 25), because of backwards compatibility.
@@leonmunster8972 I've programmed C++ for a living in the past but don't do so any more and don't depend on it in that manner, but do try to stay up to speed. However, the language am most looking into now as an alternative to C, C++, and Rust is Zig
bind and placeholders could be a better example of a 'using namespace' inside the scope of your function since bind(foo, std::placeholders::_1, std::placeholders::_2) can be difficult to read.
I have solved this once with `using ph=std::placeholders;` and `std::bind(foo, ph::_1, ph::_2)`
@@wokste nice
It would definitely fall into the "small local scope" like UDLs
LOL - your unit tests are bad! Just kidding. Couldn't agree more. Thank's a lot for the examples!
The language is wrong: Collisions should be compilation errors.
That would break many legitimate use cases for pulling functions into your overload set. Namely with the case of inheritance.
It doesn't help that a source as authoritative as Bjarne's PPP uses "using namespace std;" in its helper header. As a result every example in the book is writted as though the contents of the std:: namespace are at global scope. Even if someone knows this is a bad idea, it still makes the examples tough to follow when *not* using "using namespace std;", because the reader must work out which functions/classes must be std:: qualified, and which are elsewhere in the code.
I did not know that, that is most unfortunate
I'm going to respectfully disagree here with the "never" part. I know from experience after having written hundreds of thousands of lines of code that I've never ran into problems by using "using the::namespace::of::my::class" in the class-implementation cpp except for rare, possibly compiler-dependent, occasions where the compiler was incapable of seeing the definition of a method if it's not fully qualified (had to go void the::namespace::of::my::class::myClass::mymethod() {}"
@@karthikravikanti2922 I mispelled. I meant "using namespace the::namespace::of::my::class; So I can straight out define the methods of the class like "void myClass::myMethod()" instead of having to write "void the::namespace::of::my::class::myClass::myMethod()" for every method of that class which would be a pointless waste of time, resources and would make the definitions extremely difficult to read.
Besides, what happens when you are defining a templated class with the methods defined in the header and the class itself is nested inside a namespace scope? For the code that's inside the methods declaration that has the same effect as having written "using namespace" in the cpp for any other class definition.
@@DamianReloaded You can just write a `namespace the::namespace::of::my::class { /* … */ }` scope in your implementation files just as easily as easily as `using namespace the::namespace::of::my::class`
It achieves the exact same results, is cleaner, works across all compilers, allows you to define multiple namespaces cleanly, and avoids any issues of pulling entire namespaces into the global scope.
@@su1cidity it has the exact same drawbacks than those mentioned in the video, which in the case of .cpp class implementation file are essentially non existent when you write using namespace the::namespace::of::my::class; after the includes
@@DamianReloaded Writing the namespace scope has zero drawbacks. If you’re trying to say that `using namespace your::long::nested::namespace` is the same and doesn’t have drawbacks, then I wholeheartedly disagree.
Your using namespace solution cannot, for example, be safely used in conglomerate builds - where-as simply placing the implementation in its appropriate namespace can be.
@@su1cidity would you mind describing a scenario of conglomerate builds where using namespace; would cause collisions that you won't get using the namespace scope?
Having to write std:: isn't that bad. I mean it's still no where near jave lol.
Also: don't use "using" that is not a typedef, except in very specific cases. Always qualify the things you use. Shorten your qualifications with namespace aliases, which should be standardized through your code. Do this in other languages as well. I don't use from... import... in Python. I do module aliases, standardized.
Also: don't use ADL, use namespace injection.
ADL = bad bad bad bad.
It is impossible to escape super outdated C++ teaching materials which loved using using namespace std.
there is no such rule.
A lot of bad tips given by a lot of bad yt vids.
This should not be allowed by the language then. C++ should be renamed to "Gotcha" language :)
It’s there for the convenience of mockup code not for releases and as mentioned in the video it is not meant for a global scope whatsoever
@@Ryan-xq3kl Mock-up code? C++ is not a modelling language or something. I'd say if we should not be using it in the global scope it should not be allowed by the language. Or at least (and it's probably the best approach) it should not compile if there is a symbol resolution ambiguity.
@@Archie3D "it should not compile if there is a symbol resolution ambiguity"
THIS. In so many other contexts, the compiler will tell you that the call is ambiguous and that you have to specify which one. There's no reason it couldn't do the same here. The point of namespaces is to _prevent_ conflicts, which the compiler will complain about. If you reintroduce a conflict, it should complain again.
@@bloodgain exactly my thought.