c++ - это - пространство имен std




Почему «использование пространства имен std;» считается плохой практикой? (20)

Не используйте это глобально

Считается «плохим» только при глобальном использовании . Так как:

  • Вы загромождаете пространство имен, в котором программируете.
  • Читателям будет трудно увидеть, откуда исходит конкретный идентификатор, когда вы используете многие из них, using namespace xyz .
  • То, что верно для других читателей вашего исходного кода, еще более верно для самого частого читателя: вас. Вернитесь через год или два и посмотрите ...
  • Если вы говорите только об using namespace std вы можете не знать обо всех вещах, которые вы захватываете - и когда вы добавляете еще один #include или переходите на новую версию C ++, вы можете получить конфликты имен, о которых вы не знали.

Вы можете использовать его локально

Идите вперед и используйте его локально (почти) свободно. Это, конечно, предотвращает повторение std:: - и повторение тоже плохо.

Идиома для его локального использования

В C ++ 03 была идиома - шаблонный код - для реализации функции swap для ваших классов. Было предложено использовать локальное using namespace std или, по крайней мере, using std::swap :

class Thing {
    int    value_;
    Child  child_;
public:
    // ...
    friend void swap(Thing &a, Thing &b);
};
void swap(Thing &a, Thing &b) {
    using namespace std;      // make `std::swap` available
    // swap all members
    swap(a.value_, b.value_); // `std::stwap(int, int)`
    swap(a.child_, b.child_); // `swap(Child&,Child&)` or `std::swap(...)`
}

Это делает следующую магию:

  • Компилятор выберет std::swap для value_ , то есть void std::swap(int, int) .
  • Если у вас реализована перегрузка void swap(Child&, Child&) компилятор выберет ее.
  • Если у вас нет такой перегрузки, компилятор будет использовать void std::swap(Child&,Child&) и попытаться void std::swap(Child&,Child&) их.

В C ++ 11 больше нет причин использовать этот шаблон. Реализация std::swap была изменена, чтобы найти потенциальную перегрузку и выбрать ее.

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

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


  1. Вы должны уметь читать код, написанный людьми, которые придерживаются мнения, отличного от вашего стиля и лучших практик.

  2. Если вы используете только cout, никто не запутается. Но когда у вас много летающих пространств имен, и вы видите этот класс, и вы не совсем уверены, что он делает, явное использование пространства имен действует как своего рода комментарий. На первый взгляд вы можете видеть: «О, это операция с файловой системой» или «Это делает сеть».


Из моего опыта, если у вас есть несколько библиотек, которые используют, скажем cout , но для другой цели вы можете использовать не так cout .

Например, если я ввожу, using namespace std; и using namespace otherlib; и печатаю только cout (что происходит в обоих), а не std::cout (или 'otherlib::cout' ), вы можете использовать неправильный и получать ошибки, это гораздо более эффективно и действенно std::cout .


Использование множества пространств имен в одно и то же время является, очевидно, рецептом катастрофы, но использование пространства имен JUST std и только пространства имен std , на мой взгляд, не так уж важно, поскольку переопределение может происходить только с помощью вашего собственного кода ...

Так что просто рассматривайте их функции как зарезервированные имена, такие как «int» или «class», и все.

Люди должны перестать быть такими анальными по этому поводу. Ваш учитель был прав с самого начала. Просто используйте одно пространство имен; в этом весь смысл использования пространств имен на первом месте. Вы не должны использовать более одного одновременно. Если только это не ваше. Итак, еще раз, переопределение не произойдет.


Пример использования пространства имен std приводит к ошибке компиляции из-за неоднозначности count, которая также является функцией в библиотеке алгоритмов.

#include <iostream>

using namespace std;

int count = 1;
int main() {
    cout<<count<<endl;
}

Пространство имен - это именованная область. Пространства имен используются для группировки связанных объявлений и для разделения отдельных элементов. Например, две отдельно разработанные библиотеки могут использовать одно и то же имя для ссылки на разные элементы, но пользователь все равно может использовать оба:

namespace Mylib{
    template<class T> class Stack{ /* ... */ };
    / / ...
}
namespace Yourlib{
    class Stack{ /* ... */ };
    / / ...
}
void f(int max) {
    Mylib: :Stack<int> s1(max) ; / / use my stack
    Yourlib: :Stack s2(max) ; / / use your stack
    / / ...
}

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

void f(int max) {
    using namespace Mylib; / / make names from Mylib accessible
    Stack<int> s1(max) ; / / use my stack
    Yourlib: :Stack s2(max) ; / / use your stack
    / / ...
}

Пространства имен предоставляют мощный инструмент для управления различными библиотеками и различными версиями кода. В частности, они предлагают программисту альтернативы того, как явно сделать ссылку на нелокальное имя.

Источник: Обзор языка программирования C ++, Бьярн Страуструп


Чтобы ответить на ваш вопрос, я смотрю на это практически: многие программисты (не все) используют пространство имен std. Поэтому следует иметь привычку НЕ использовать вещи, которые сталкиваются или используют те же имена, что и в пространстве имен std. Это вполне допустимо, но не так много по сравнению с количеством возможных связных слов и псевдонимов, которые можно сформулировать строго говоря.

Я имею в виду на самом деле ... говорить "не полагайся на это присутствие" - это просто настраивать тебя на то, чтобы он НЕ присутствовал. У вас постоянно будут проблемы с заимствованием фрагментов кода и их постоянным исправлением. Просто держите ваши пользовательские и заимствованные вещи в ограниченном объеме, как они должны быть, и ОЧЕНЬ щадите глобальные (честно говоря, глобальные переменные почти всегда должны быть последним средством для целей «скомпилируйте сейчас, рассудите позже»). Действительно, я думаю, что это плохой совет от вашего учителя, потому что использование std будет работать как для "cout", так и для "std :: cout", но НЕ использование std будет работать только для "std :: cout". Вам не всегда повезет написать свой собственный код.

ПРИМЕЧАНИЕ. Не зацикливайтесь на вопросах эффективности, пока вы не узнаете немного о том, как работают компиляторы. Имея небольшой опыт написания кода, вам не нужно много узнавать о них, прежде чем вы поймете, насколько они способны обобщить хороший код в нечто простое. Все так же просто, как если бы вы написали все это на языке C. Хороший код настолько сложен, насколько это необходимо.


Это зависит от того, где он находится. Если это общий заголовок, то вы уменьшаете значение пространства имен, объединяя его с глобальным пространством имен. Имейте в виду, это может быть аккуратный способ создания глобальных модулей.


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


Я не думаю, что это обязательно плохая практика в любых условиях, но вы должны быть осторожны, когда используете ее. Если вы пишете библиотеку, вам, вероятно, следует использовать операторы разрешения области действия с пространством имен, чтобы ваша библиотека не сталкивалась с другими библиотеками. Что касается кода уровня приложения, я не вижу в этом ничего плохого.


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

Я обычно использую его в своем объявлении класса, так как методы в классе имеют тенденцию иметь дело с подобными типами данных (членами), а typedef - это возможность назначить имя, которое имеет смысл в контексте класса. Это на самом деле помогает удобочитаемости в определениях методов класса.

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

и в реализации:

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

в отличие от:

//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;
}

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

Лошади для курсов - управляй своей сложностью так, как ты можешь и можешь чувствовать себя лучше.


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

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


Краткая версия: не используйте глобальное использование объявлений или директив в заголовочных файлах. Не стесняйтесь использовать их в файлах реализации. Вот что Херб Саттер и Андрей Александреску должны сказать об этой проблеме в стандартах кодирования C ++ (выделение для акцента мое):

Резюме

Использование пространства имен для вашего удобства, а не для того, чтобы вы навязывали его другим. Никогда не пишите объявление использования или директиву использования перед директивой #include.

Следствие: в заголовочных файлах не пишите на уровне пространства имен, используя директивы или объявления; вместо этого явно определите пространство имен для всех имен. (Второе правило следует из первого, потому что заголовки никогда не могут знать, какие другие заголовки #include могут появиться после них.)

обсуждение

Вкратце: вы можете и должны использовать пространство имен, свободно используя объявления и директивы в ваших файлах реализации после директив #include, и вам это нравится. Несмотря на неоднократные утверждения об обратном, пространства имен, использующие объявления и директивы, не являются злом и не наносят ущерба цели пространств имен. Скорее, именно они делают пространства имен пригодными для использования .


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

using namespace std;
using namespace boost;

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

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


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

Опытные программисты также стараются избегать полной квалификации имен внутри своих исходных файлов. Небольшая причина этого заключается в том, что не элегантно писать больше кода, когда недостаточно кода, если нет веских причин . Основной причиной этого является отключение аргумент-зависимого поиска (ADL).

Каковы эти веские причины ? Иногда программисты явно хотят отключить ADL, иногда они хотят устранить неоднозначность.

Итак, все в порядке:

  1. Директивы использования уровня функций и объявления использования внутри реализаций функций
  2. Объявления использования на уровне исходного файла внутри исходных файлов
  3. (Иногда) директивы using уровня исходного файла

Проблема с using namespace в файлах заголовков ваших классов заключается в том, что это заставляет любого, кто хочет использовать ваши классы (включая файлы заголовков), также «использовать» (то есть видеть все в) эти другие пространства имен.

Тем не менее, вы можете свободно использовать оператор using в своих (приватных) * .cpp файлах.

Имейте в виду, что некоторые люди не согласны с моим высказыванием «не стесняйтесь», как это - потому что, хотя оператор использования в файле cpp лучше, чем в заголовке (потому что это не влияет на людей, которые включают ваш заголовочный файл), они думают, что это все еще не хорошо (потому что в зависимости от кода это может затруднить поддержку реализации класса). Эта тема часто задаваемых вопросов говорит,

Директива using существует для унаследованного кода C ++ и для облегчения перехода к пространствам имен, но вам, вероятно, не следует использовать его на регулярной основе, по крайней мере, в новом коде C ++.

FAQ предлагает две альтернативы:

  • Декларация об использовании:

    using std::cout; // a using-declaration lets you use cout without qualification
    cout << "Values:";
  • Просто наберите std ::

    std::cout << "Values:";

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

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


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

class stringstream {  // uh oh
};

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

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


Я согласен с тем, что его не следует использовать глобально, но не так страшно использовать его локально, как в 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 таких как His_lib::String ), имеют приоритет над именами, доступными в другой области действия с помощью директивы using namespace Her_lib ( using namespace Her_lib ).


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

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

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

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

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

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

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





c++-faq