Подтвердить что ты не робот

Использование std-пространства имен

По-видимому, существуют разные представления об использовании "использования" в отношении пространства имен std.

Некоторые говорят, что используют 'using namespace std', другие говорят, что это не префиксные функции std, которые должны использоваться с 'std::', в то время как другие говорят, что-то вроде этого:

using std::string;
using std::cout;
using std::cin;
using std::endl;
using std::vector;

для всех функций std, которые должны использоваться.

Каковы плюсы и минусы каждого? |

4b9b3361

Ответ 1

Большинство пользователей С++ довольны чтением std::string, std::vector и т.д. Фактически, просмотр raw vector заставляет меня задаться вопросом, является ли это std::vector или другим пользовательским vector.

Я всегда против использования using namespace std;. Он импортирует все виды имен в глобальное пространство имен и может вызывать всевозможные неочевидные двусмысленности.

Вот некоторые общие идентификаторы, которые находятся в пространстве имен std: count, sort, find, equal, reverse. Наличие локальной переменной с именем count означает, что using namespace std не позволит вам использовать count вместо std::count.

Классическим примером конфликта нежелательных имен является следующее. Представьте, что вы новичок и не знаете о std::count. Представьте, что вы либо используете что-то еще в <algorithm>, либо втянули его, казалось бы, несвязанный заголовок.

#include <algorithm>
using namespace std;

int count = 0;

int increment()
{
    return ++count; // error, identifier count is ambiguous
}

Ошибка обычно длинная и недружественная, потому что std::count - это шаблон с несколькими длинными вложенными типами.

Это нормально, потому что std::count переходит в глобальное пространство имен, и подсчет функции скрывает его.

#include <algorithm>
using namespace std;

int increment()
{
    static int count = 0;
    return ++count;
}

Возможно, немного удивительно, это нормально. Идентификаторы, импортированные в декларативную область, отображаются в общем пространстве имен, которое охватывает оба параметра, где они определены, и где они импортируются. Другими словами, std::count отображается как count в глобальном пространстве имен, но только внутри increment.

#include <algorithm>

int increment()
{
    using namespace std;
    static int count = 0;
    return ++count;
}

И по тем же причинам count здесь неоднозначный. using namespace std не вызывает std::count, скрывает внешний count, как и следовало ожидать. Правило using namespace означает, что std::count выглядит (в функции increment), как если бы он был объявлен в глобальной области видимости, т.е. В той же области, что и int count = 0; и, следовательно, вызывает неоднозначность.

#include <algorithm>

int count = 0;

int increment()
{
    using namespace std;
    return ++count; // error ambiguous
}

Ответ 2

Исключая основы (Чтобы добавить std:: infront всех stl-объектов/функций и меньше шансов на конфликт, если у вас нет 'using namespace std')

Также стоит отметить, что вы никогда не должны ставить

using namespace std

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

В некоторых случаях очень полезно использовать такие вещи, как

using std::swap

Как будто есть специальная версия swap, компилятор будет использовать это, иначе он вернется на std:: swap

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

Ответ 3

Во-первых, некоторая терминология:

  • Использование-декларация: using std::vector;
  • using-directive: using namespace std;

Я думаю, что использование using-директив отлично, если они не используются в глобальной области видимости в файле заголовка. Таким образом, имея

using namespace std;

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

Аналогично, если вы можете использовать несколько using-declarations (вместо using-directives) для определенных типов в пространстве имен std, тогда нет причина, по которой вы не должны иметь только те специфические имена, которые были введены в текущее пространство имен. Точно так же, я думаю, что это было бы сумасшествием и хлопот бухгалтерии, чтобы иметь 25 или 30 деклараций использования, когда одна директива-использование тоже могла бы сделать трюк.

Также хорошо иметь в виду, что бывают случаи, когда вы должны использовать декларацию использования. Обратитесь к статье Скотта Мейерса "Пункт 25: Рассмотрите поддержку неметающего свопа" из Effective С++, Third Edition. Чтобы иметь общую шаблонную функцию, используйте "лучший" метод свопинга для параметризованного типа, вам нужно использовать поиск с использованием объявления и зависящий от аргумента поиск (например, поиск ADL или Koenig):

template< typename T >
void foo( T& x, T& y)
{
    using std::swap;     // makes std::swap available in this function

    // do stuff...

    swap( x, y);         // will use a T-specific swap() if it exists,
                         //  otherwise will use std::swap<T>()

    // ...
 }

Я думаю, что мы должны смотреть на общие идиомы для разных языков, которые существенно используют пространства имен. Например, Java и С# используют пространства имен в значительной степени (возможно, более чем С++). Наиболее распространенные имена имен в пространствах имен используются на этих языках, путем массового ввода их в текущую область с эквивалентом директивы using. Это не вызывает широко распространенных проблем, и несколько раз это проблема, обрабатывается на основе "исключения", рассматривая имена, о которых идет речь, с помощью полностью квалифицированных имен или псевдонимов - точно так же, как это можно сделать на С++.

Херб Саттер и Андрей Александреску говорят об этом в "Пункт 59: Не пишите имена пространства имен в файле заголовка или перед #include" своей книги, С++ Coding Standards: 101 Rules, Guidelines and Best Practices:

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

Stroupstrup часто цитируется как "Не загрязнять глобальное пространство имен", в "Язык программирования С++, третье издание". Фактически он говорит, что (C.14 [15]), но относится к главе C.10.1, где он говорит:

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

namespaceX {
    int i , j , k ;
}

int k ;
void f1()
{
    int i = 0 ;

    using namespaceX ; // make names from X accessible

    i++; // local i
    j++; // X::j
    k++; // error: X::k or global k ?

    ::k ++; // the global k

    X::k ++; // X’s k
}

void f2()
{
    int i = 0 ;

    using X::i ; // error: i declared twice in f2()
    using X::j ;
    using X::k ; // hides global k

    i++;
    j++; // X::j
    k++; // X::k
}

Локально объявленное имя (объявлено либо обычной декларацией, либо с помощью объявления-объявления) скрывает нелокальные объявления с одноименным названием и любые незаконные перегрузки имени обнаружено в точке объявления.

Обратите внимание на ошибку неоднозначности для k++ в f1(). Глобальные имена не указаны предпочтение по именам из пространств имен доступный в глобальном масштабе. Это обеспечивает значительную защиту против случайных столкновений имен, и - что важно - обеспечивает нет никаких преимуществ, которые можно получить от загрязнение глобального пространства имен.

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

...

Я надеюсь увидеть радикальное снижение использование глобальных имен в новых программ с использованием пространств имен по сравнению с традиционные программы на C и С++. правила для пространств имен были конкретно созданный, чтобы не давать никаких преимуществ "Ленивый пользователь глобальных имен кто-то, кто заботится о том, чтобы не загрязнять глобальный охват.

И как у кого-то есть то же преимущество, что и "ленивый пользователь глобальных имен"? Воспользовавшись директивой use, которая безопасно делает имена в пространстве имен доступными для текущей области.

Обратите внимание, что имена различий в пространстве имен std, доступное для области с правильным использованием директивы using (путем размещения директивы после #includes), не загрязняют глобальное пространство имен. Это просто делает эти имена доступными легко и с постоянной защитой от столкновений.

Ответ 4

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

В файле реализации выбор гораздо менее режет.

  • Помещение пространства имен using std выводит все символы из этих пространств имен. Это может быть хлопотно, поскольку почти никому не известно все символы, которые есть (так что политика без конфликтов невозможно применить на практике), не говоря уже о символах, которые будут добавлены. И стандарт С++ позволяет заголовку добавлять символы из других заголовков (C этого не позволяет). Он по-прежнему может хорошо работать на практике, чтобы упростить запись в контролируемом случае. И если возникла ошибка, она обнаруживается в файле, который имеет проблему.

  • Использование std:: name; имеет преимущество простоты написания без риска импорта неизвестных символов. Стоимость заключается в том, что вам нужно явно импортировать все нужные символы.

  • Явная квалификация добавляет немного беспорядка, но я думаю, что чем меньше проблем, тем лучше.

В моем проекте я использую явную квалификацию для всех имен, я принимаю использование std:: name, я борюсь против использования пространства имен std (у нас есть интерпретатор lisp, у которого есть свой собственный тип списка, и поэтому конфликт - это верная вещь).

Для других пространств имен вы также должны учитывать используемые соглашения об именах. Я знаю проект, который использует пространство имен (для версий) и префикс для имен. Выполнение using namespace X тогда почти без риска и не делает этого приводит к глупому выглядящему коду PrefixNS::pfxMyFunction(...).

Есть случаи, когда вы хотите импортировать символы. std:: swap - наиболее распространенный случай: вы импортируете std:: swap, а затем используете swap unqualified. Аргумент зависимого поиска найдет адекватную свопинг в пространстве имен этого типа, если он есть, и вернуться к стандартным шаблонам, если их нет.


Edit:

В комментариях Майкл Бэрр задается вопросом, происходят ли конфликты в реальном мире. Вот настоящий живой пример. У нас есть язык расширения с диалектом lisp. Наш интерпретатор имеет файл include, lisp.h содержащий

typedef struct list {} list;

Нам пришлось интегрировать и адаптировать некоторый код (который я назову "движок" ), который выглядит так:

#include <list>
...
using std::list;
...
void foo(list const&) {}

Итак, мы изменили так:

#include <list>

#include "module.h"
...
using std::list;
...
void foo(list const&) {}

Хорошо. Все работает. Несколько месяцев спустя "module.h" был изменен, чтобы включить "list.h". Испытания прошли. "модуль" не был изменен таким образом, который повлиял на его ABI, поэтому можно было бы использовать библиотеку "engine" без повторной компиляции своих пользователей. Тесты интеграции были в порядке. Вышел новый "модуль". Следующая компиляция двигателя сломалась, когда его код не был изменен.

Ответ 5

И

using std::string;

и

using namespace std;

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

В реализации (.cpp) файлы для вас (помните, что нужно это делать после всех директив #include). Вы можете разбить только код в этом конкретном файле, чтобы упростить управление и выяснить причину конфликта имен. Если вы предпочитаете использовать std:: (или любой другой префикс, может быть много пространств имен в вашем проекте) до indentifiers, это нормально. Если вы хотите добавить идентификаторы, которые вы используете для глобального пространства имен, это нормально. Если вы хотите создать целую область имен на голове:-), это зависит от вас. Хотя эффекты ограничены единым модулем компиляции, это приемлемо.

Ответ 6

Если у вас нет риска конфликтов имен в вашем коде с помощью std и других библиотек, вы можете использовать:

using namespace std;

Но если вы хотите точно знать зависимость вашего кода от документации или существует риск конфликтов имен, используйте другой способ:

using std::string;
using std::cout;

Третье решение, не используйте эти решения и пишите std:: перед тем, как каждое использование кода приведет к большей безопасности, но, может быть, немного тяжести в коде...

Ответ 7

Для меня я предпочитаю использовать ::, когда это возможно.

std::list<int> iList;

Мне не нравится писать:

for(std::list<int>::iterator i = iList.begin(); i != iList.end(); i++)
{
    //
}

Надеюсь, с С++ 0x я бы написал следующее:

for(auto i = iList.begin(); i != iList.end(); i++)
{
    //
}

Если пространство имен очень длинное,

namespace dir = boost::filesystem;

dir::directory_iterator file("e:/boost");
dir::directory_iterator end;

for( ; file != end; file++)
{
    if(dir::is_directory(*file))
        std::cout << *file << std::endl;
}

Ответ 8

Вы никогда не должны быть using namespace std в области пространства имен в заголовке. Кроме того, я полагаю, большинство программистов будут задаваться вопросом, когда они видят vector или string без std::, поэтому я думаю, что using namespace std лучше. Поэтому я утверждаю, что никогда не был using namespace std вообще.

Если вам кажется, что вы должны добавить локальные объявления, например using std::vector. Но спросите себя: что это стоит? Строка кода записывается один раз (может быть, дважды), но она читается десять, сто или тысяча раз. Сохраненное усилие ввода будет добавлением объявления или директивы использования, является маргинальным по сравнению с усилием чтения кода.

Учитывая это, в проекте десять лет назад мы решили явно квалифицировать все идентификаторы с их полными именами пространства имен. Казалось, что сначала неловко становилось обычным в течение двух недель. Теперь во всех проектах этой компании никто больше не использует директивы или декларации. (За одним исключением, см. Ниже.) Глядя на код (несколько MLoC) через десять лет, я чувствую, что мы приняли правильное решение.

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

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

Ответ 9

using namespace std импортирует содержимое пространства имен std в текущем. Таким образом, преимущество заключается в том, что вам не нужно вводить std:: перед всеми функциями этого пространства имен. Однако может случиться, что у вас есть разные пространства имен, которые имеют функции с тем же именем. Таким образом, вы можете не называть тот, который вам нужен.

Задание вручную тех, которые вы хотите импортировать в std, предотвращает это, но может привести к длинному списку использования в начале вашего файла, который какой-нибудь разработчик найдет уродливым;)!

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

EDIT: как указано в другом ответе, вы никогда не должны помещать using namespace в заголовочный файл, поскольку он будет распространяться на все файлы, включая этот заголовок, и таким образом может вызвать нежелательное поведение.

EDIT2: исправил мой ответ, спасибо комментарию Чарльза.

Ответ 10

Пространства имен содержат код, содержащийся для предотвращения путаницы и загрязнения сигнатур функций.

Здесь полная и документально подтвержденная демонстрация надлежащего namespace:

#include <iostream>
#include <cmath>  // Uses ::log, which would be the log() here if it were not in a namespace, see /questions/59708/why-is-my-log-in-the-std-namespace

// Silently overrides std::log
//double log(double d) { return 420; }

namespace uniquename {
    using namespace std;  // So we don't have to waste space on std:: when not needed.

    double log(double d) {
        return 42;
    }

    int main() {
        cout << "Our log: " << log(4.2) << endl;
        cout << "Standard log: " << std::log(4.2);
        return 0;
    }
}

// Global wrapper for our contained code.
int main() {
    return uniquename::main();
}

Вывод:

Our log: 42
Standard log: 1.43508

Ответ 11

Как и на Java, где вы можете использовать, вы можете включить java.util. * или просто выбрать каждый класс отдельно, это зависит от стиля. Обратите внимание, что вам не нужен один using namespace std в начале вашего файла/большой области, потому что вы будете загрязнять пространство имен и, возможно, столкнуться, побеждая точку пространства имен. Но если у вас есть функция, которая использует много STL, она загромождает код, чтобы иметь беспорядок синтаксиса префикса в вашей логике, и вам, вероятно, следует рассмотреть возможность использования либо using namespace std (при использовании различных классов), либо индивидуального using (часто используется несколько классов).

Ответ 12

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

Это потому, что то, что вы хотите, чтобы ваш код выглядел, зависит от поставленной задачи.

Создавая исходный код, я предпочитаю видеть, какой именно класс я использую: это std::string или BuzFlox::Obs::string class?

При разработке потока управления меня даже не интересуют типы переменных, но я хочу сосредоточиться на if и while и continue.

Итак, это мой совет:

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

Ответ 13

Есть несколько способов исправить это.

Сначала: используйте то же, что и вы.

Во-вторых: do namespace S = std;, уменьшая 2 символа.

В-третьих: используйте static.

В-четвертых: не используйте имена, используемые std.

Ответ 14

Каковы плюсы и минусы каждого

Единственная причина отказаться от std:: заключается в том, что теоретически вы могли бы полностью реализовать все функции STL. Тогда ваши функции можно было бы переключить с помощью std::vector на my:: vector без изменения кода.

Ответ 15

Почему не, например,

typedef std::vector<int> ints_t;
ints_t ints1;
....
ints_t ints2;

вместо громоздких

std::vector<int> ints1;
...
std::vector<int> ints2;

Я нахожу это гораздо более удобочитаемым и свой стандарт для кодирования.

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

void getHistorgram(std::vector<unsigned int>&, std::vector<unsigned int>&);

какие из них возвращают значение?

Как об этом

typedef std::vector<unsigned int> values_t;
typedef std::vector<unsigned int> histogram_t;
...
void getHistogram(values_t&, histogram_t&);