[C++] Почему «использование пространства имен std» считается плохой практикой?


Answers

Я согласен со всем, что написал Грег , но я бы хотел добавить: это может даже ухудшиться, чем сказал Грег!

Библиотека Foo 2.0 могла бы ввести функцию Quux() , которая является однозначно лучшим совпадением для некоторых ваших вызовов Quux() чем bar::Quux() код которых вызывается в течение многих лет. Затем ваш код все еще компилируется , но он молча вызывает неправильную функцию и бог знает-что. Это примерно так же плохо, как все может быть.

Имейте в виду, что в пространстве имен std есть множество идентификаторов, многие из которых являются очень распространенными ( list мысли, sort , string , iterator и т. Д.), Которые также могут появиться в другом коде.

Если вы считаете, что это маловероятно: здесь был задан вопрос о переполнении стека, где почти точно это произошло (неправильная функция, вызванная опущенным std:: prefix) примерно через полгода после того, как я дал этот ответ. Here еще один, более свежий пример такого вопроса. Так что это настоящая проблема.

Вот еще одна точка данных. Многие, много лет назад, я также привык считать, что это раздражает необходимость префикса из стандартной библиотеки с помощью std:: . Затем я работал в проекте, где вначале было решено, что using директив и деклараций запрещено, за исключением областей функций. Угадай, что? Большинству из нас было очень мало недель, чтобы привыкнуть писать префикс, и через несколько недель большинство из нас даже согласилось с тем, что он действительно сделал код более удобочитаемым . Есть причина для этого: нравится ли вам более короткая или длинная проза, субъективна, но префиксы объективно добавляют ясность в код. Не только компилятор, но и вам также легче увидеть, к какому идентификатору относится.

За десять лет этот проект вырос до нескольких миллионов строк кода. Поскольку эти обсуждения возникают снова и снова, мне когда-то было любопытно, как часто используется (разрешенная) функция-область, using самом деле в проекте. Я нашел источники для этого и нашел только один или два десятка мест, где он использовался. Для меня это указывает на то, что, когда-то пробовавшие, разработчики не находят std:: достаточно болезненным, чтобы использовать директивы даже один раз каждые 100 kLoC даже там, где это позволялось использовать.

Итог: Явно префиксное все не наносит никакого вреда, очень мало привыкает и имеет объективные преимущества. В частности, это упрощает интерпретацию кода компилятором и человеческими читателями - и это, вероятно, должно быть главной целью при написании кода.

Question

Мне говорили другие, что писать using namespace std в коде неверно, и что я должен использовать вместо него std::cout и std::cin .

Почему using namespace std считается плохой практикой? Является ли он неэффективным или он может объявлять неоднозначные переменные (переменные, которые имеют одно и то же имя как функция в пространстве имен std )? Это влияет на производительность?




Рассматривать

// myHeader.h
#include <sstream>
using namespace std;


// someoneElses.cpp/h
#include "myHeader.h"

class stringstream {  // uh oh
};

Обратите внимание, что это простой пример: если у вас есть файлы с 20 включенными и другими импортами, у вас будет много зависимостей, чтобы разобраться в проблеме. Хуже всего то, что вы можете получить несвязанные ошибки в других модулях в зависимости от определений, которые конфликтуют.

Это не ужасно, но вы избавитесь от головных болей, не используя его в файлах заголовков или в глобальном пространстве имен. Скорее всего, это можно сделать в очень ограниченных областях, но у меня никогда не было проблем с набором дополнительных 5 символов, чтобы уточнить, откуда мои функции.




Нельзя использовать директиву в глобальном масштабе, особенно в заголовках. Однако есть ситуации, когда это подходит даже в файле заголовка:

template <typename FloatType> inline
FloatType compute_something(FloatType x)
{
    using namespace std; //no problem since scope is limited
    return exp(x) * (sin(x) - cos(x * 2) + sin(x * 3) - cos(x * 4));
}

Это лучше, чем явная квалификация ( std::sin , std::cos ...), потому что она короче и имеет возможность работать с определенными пользователем типами с плавающей запятой (через зависимый от аргумента поиск).




I do not think it is necessarily bad practice under all conditions, but you need to be careful when you use it. If you're writing a library, you probably should use the scope resolution operators with the namespace to keep your library from butting heads with other libraries. For application level code, I don't see anything wrong with it.




Если вы импортируете правильные файлы заголовков, у вас внезапно появляются имена, такие как hex , left , plus или count в вашей глобальной области. Это может быть удивительно, если вы не знаете, что std:: содержит эти имена. Если вы также попытаетесь использовать эти имена локально, это может привести к некоторой путанице.

Если все стандартные материалы находятся в собственном пространстве имен, вам не нужно беспокоиться о конфликтах имен с вашим кодом или другими библиотеками.




Недавно я столкнулся с жалобой на Visual Studio 2010 . Оказалось, что почти все исходные файлы имеют две строки:

using namespace std;
using namespace boost;

Многие функции Boost входят в стандарт C ++ 0x, а в Visual Studio 2010 много возможностей C ++ 0x, поэтому эти программы не компилируются.

Поэтому, избегая using namespace X; является формой будущей проверки, способом убедиться в том, что изменение в библиотеках и / или файлах заголовков, которые используются, не нарушит работу программы.




Приятно видеть код и знать, что он делает. Если я вижу std::cout я знаю, что это поток cout библиотеки std . Если я увижу cout то я не знаю. Это может быть поток cout библиотеки std . Или может быть int cout = 0; десять линий выше в той же функции. Или static переменная cout в этом файле. Это может быть что угодно.

Теперь возьмите миллионную строку кода, которая не особенно велика, и вы ищете ошибку, а это означает, что вы знаете, что есть одна строка в этом миллионе строк, которая не делает то, что она должна делать. cout << 1; мог прочитать static int named cout , сдвинуть его влево на один бит и выбросить результат. Глядя на ошибку, я должен проверить это. Вы видите, как я действительно предпочитаю видеть std::cout ?

Это одна из этих вещей, которые кажутся действительно хорошей идеей, если вы учитель и никогда не должны писать и поддерживать какой-либо код для жизни. Мне нравится видеть код, где (1) я знаю, что он делает; и, (2) я уверен, что человек, пишущий это, знал, что он делает.




Конкретный пример для выяснения обеспокоенности. Представьте, что у вас есть ситуация, когда у вас есть 2 библиотеки, foo и bar, каждая из которых имеет собственное пространство имен:

namespace foo {
    void a(float) { /* does something */ }
}

namespace bar {
    ...
}

Теперь предположим, что вы используете foo и bar вместе в своей собственной программе следующим образом:

using namespace foo;
using namespace bar;

void main() {
    a(42);
}

На данный момент все в порядке. Когда вы запускаете свою программу, она «делает что-то». Но позже вы обновляете панель и, скажем так, изменились:

namespace bar {
    void a(float) { /* does something completely different */ }
}

На этом этапе вы получите ошибку компилятора:

using namespace foo;
using namespace bar;

void main() {
    a(42);  // error: call to 'a' is ambiguous, should be foo::a(42)
}

So you'll need to do some maintenance to clarify which 'a' you meant (ie foo::a ). That's probably undesirable, but fortunately it is pretty easy (just add foo:: in front of all calls to a that the compiler marks as ambiguous).

But imagine an alternative scenario where bar changed instead to look like this instead:

namespace bar {
    void a(int) { /* does something completely different */ }
}

At this point your call to a(42) suddenly binds to bar::a instead of foo::a and instead of doing 'something' it does 'something completely different'. No compiler warning or anything. Your program just silently starts doing something complete different than before.

When you use a namespace you're risking a scenario like this, which is why people are uncomfortable using namespaces. The more things in a namespace the greater the risk of conflict, so people might be even more uncomfortable using namespace std (due to the number of things in that namespace) than other namespaces.

Ultimately this is a trade-off between writability vs reliability/maintainability. Readability may factor in also, but I could see arguments for that going either way. Normally I would say reliability and maintainability are more important, but in this case you'll constantly pay the writability cost for an fairly rare reliability/maintainability impact. The 'best' trade-off will determine on your project and your priorities.




I agree with the others here, but would like to address the concerns regarding readability - you can avoid all of that by simply using typedefs at the top of your file, function or class declaration.

I usually use it in my class declaration as methods in a class tend to deal with similar data types (the members) and a typedef is an opportunity to assign a name that is meaningful in the context of the class. This actually aids readability in the definitions of the class methods.

//header
class File
{
   typedef std::vector<std::string> Lines;
   Lines ReadLines();
}

and in the implementation:

//cpp
Lines File::ReadLines()
{
    Lines lines;
    //get them...
    return lines;
}

as opposed to:

//cpp
vector<string> File::ReadLines()
{
    vector<string> lines;
    //get them...
    return lines;
}

или:

//cpp
std::vector<std::string> File::ReadLines()
{
    std::vector<std::string> lines;
    //get them...
    return lines;
}



It doesn't make worse your software or project performance, the inclution of the namespace at the begginning of your source code isn't bad. The inclution of the using namespace std instruction varies according your needs and the way you are developing the software or project.

The namespace std contains the C++ standart functions and variables. This namespace is usefull when you often would use the C++ standart functions.

As is mentioned in this page :

The statement using namespace std is generally considered bad practice. The alternative to this statement is to specify the namespace to which the identifier belongs using the scope operator(::) each time we declare a type.

And see this opinion :

There is no problem using "using namespace std" in your source file when you make heavy use of the namespace and know for sure that nothing will collide.

Some people had said that is a bad practice to include the using namespace std in your source files because you're invoking from that namespace all the functions and variables. When you would like to define a new function with the same name of another function contained in the namespace std you would overload the function and it could produce problems due to compile or execute. It will not compile or executing as you expect.

As is mentioned in this page :

Although the statement statement saves us from typing std:: whenever we wish to access a class or type defined in the std namespace, it imports the entirety of the std namespace into the current namespace of the program. Let us take a few examples to understand why this might not be such a good thing

...

Теперь на более позднем этапе разработки мы хотим использовать другую версию cout, которая реализована в некоторой библиотеке под названием «foo» (например)

...

Обратите внимание на то, что существует двусмысленность, на которую указывает точка cout. Компилятор может обнаружить это, а не компилировать программу. В худшем случае программа все еще может компилироваться, но вызывает неправильную функцию, поскольку мы никогда не указывали, к какому пространству имен принадлежал идентификатор.




From my experiences, if you have multiple libraries that uses say, cout , but for a different purpose you may use the wrong cout .

For example, if I type in, using namespace std; and using namespace otherlib; and type just cout (which happens to be in both), rather than std::cout (or 'otherlib::cout' ), you might use the wrong one, and get errors, it's much more effective and efficient to use std::cout .




With unqualified imported identifiers you need external search tools like grep to find out where identifiers are declared. This makes reasoning about program correctness harder.




I agree with others - it is asking for name clashes, ambiguities and then the fact is it is less explicit. While I can see the use of using ... my personal preference is to limit it. I would also strongly consider what some others pointed out:

If you want to find a function name that might be a fairly common name, but you only want to find it in std namespace (or the reverse: you want to change all calls that are NOT in namespace std, namespace X, ...), then how do you propose to do this? You could write a program to do it but wouldn't it be better to spend time working on your project itself rather than writing a program to maintain your project?

Personally I actually don't mind the std:: prefix. I like the look more than not. I don't know if that is because it is explicit and says to me "this isn't my code... I am using the standard library" or if it is something else, but I think it looks nicer. This might be odd given that I only recently got in to C++ (used and still do C and other languages for much longer and C is my favourite language of all time, right above assembly).

There is one other thing although it is somewhat related to the above and what others point out. While this might be bad practise, I sometimes reserve std::name for standard library version and name for program-specific implementation. Yes indeed this could bite you and bite you hard but it all comes down to that I started this project from scratch and I'm the only programmer for it. Example: I overload std::string and call it string. I have helpful additions. I did it in part because of my C and Unix (+ Linux) tendency towards lower-case names.

Besides that, you can have namespace aliases. Here is an example of where it is useful that might not have been referred to (I didn't read all the responses and I'm having to rush off for a while in a moment). I use the C++11 standard and specifically with libstdc++. Well check this. It doesn't have complete std::regex support. Sure it compiles but it throws an exception along the lines of it being an error on the programmer's end. But it is lack of implementation. So here's how I solved it. Install boost's regex, link in boost's regex. Then, I do the following so that when libstdc++ has it implemented entirely, I need only remove this block and the code remains the same:

namespace std
{
    using boost::regex;
    using boost::regex_error;
    using boost::regex_replace;
    using boost::regex_search;
    using boost::regex_match;
    using boost::smatch;
    namespace regex_constants = boost::regex_constants;  
}

I won't argue on whether that is a bad idea or not. I will however argue that it keeps it clean for MY project and at the same time makes it specific: True I have to use boost BUT I'm using it like the libstdc++ will eventually have it. Yes, starting your own project and starting with a standard (...) at the very beginning goes a very long way with helping maintenance, development and everything involved with the project!

Edit: Now that I have time, just to clarify something. I don't actually think it is a good idea to use a name of a class/whatever in the STL deliberately and more specifically in place of. string is the exception (ignore the first, above, or second here, pun if you must) for me as I didn't like the idea of 'String'. As it is, I am still very biased towards C and biased against C++. Sparing details, much of what I work on fits C more (but it was a good exercise and a good way to make myself a. learn another language and b. try not be less biased against object/classes/etc which is maybe better stated as: less closed-minded, less arrogant, more accepting.). But what IS useful is what some already suggested: I do indeed use list (it is fairly generic, is it not ?), sort (same thing) to name two that would cause a name clash if I were to do "using namespace std;" and so to that end I prefer being specific, in control and knowing that if I intend it to be the standard use then I will have to specify it. Put simply: no assuming allowed.

And as for making boost's regex part of std. I do that for future integration and - again, I admit fully this is bias - I don't think it is as ugly as boost::regex:: ... Indeed that is another thing for me. There's many things in C++ that I still have yet to come to fully accept in looks and methods (another example: variadic templates versus var args [though I admit variadic templates are very very useful!]). Even those that I do accept it was difficult AND I still have issues with them.




Я согласен с тем, что его нельзя использовать глобально, но не так зло использовать локально, как в namespace . Вот пример из «Язык программирования C ++» :

namespace My_lib {

    using namespace His_lib; // everything from His_lib
    using namespace Her_lib; // everything from Her_lib

    using His_lib::String; // resolve potential clash in favor of His_lib
    using Her_lib::Vector; // resolve potential clash in favor of Her_lib

}

В этом примере мы разрешили потенциальные столкновения имен и двусмысленности, возникающие из их состава.

Имена, явно объявленные там (включая имена, объявленные с помощью объявлений-объявлений, таких как His_lib::String ), имеют приоритет над именами, доступными в другой области с помощью директивы using namespace Her_lib ( using namespace Her_lib ).




Я также считаю это плохой практикой. Зачем? Только однажды я подумал, что функция пространства имен - это разделить материал, чтобы я не портил его, выбросив все в одну глобальную сумку. Однако, если я часто использую «cout» и «cin», я пишу: using std::cout; using std::cin; using std::cout; using std::cin; в файле cpp (никогда в файле заголовка, поскольку он распространяется с #include ). Я думаю, что никто из здравомыслящих людей никогда не cout поток cout или cin . ;)