Когда использовать "::" для глобальной области видимости в С++?

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

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

Однако я увидел то, что заставило меня думать сегодня. Все рассматриваемые классы были обернуты в пространство имен для проекта (хорошо!), Но я видел обильное использование оператора глобальной области видимости. А именно, он использовался для всего из библиотек C (за исключением uint8_t и т.д.) Да, программист использовал .h версию этой библиотеки, так как, по-видимому, версия g++, которую они запускали, все еще бросала предупреждения о новом С++ стандарт). Это полезно? Я рассматриваю это так же, как пустую трату символов (напоминает мне об использовании этого указателя... кроме случая с конструктором копирования и оператором присваивания, где он помогает в выяснении того, из какого объекта это). Я что-то упускаю? Конечно, кто-то может прийти и назвать что-то по строкам usleep() или stderr (где я видел наибольшее использование "::" ), но разве они не узнают, что это, вероятно, сломает что-то ужасно? В какой момент вы говорите "вверните его" с точки зрения оператора сферы и просто скажите себе, что кто-то, кто называет функцию определенным образом в вашем пространстве имен, просит неприятностей?

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

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

Ответы

Ответ 1

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

Могут быть некоторые случаи (например, внутри шаблона), где вы беспокоитесь о том, что ADL вызывает неоднозначность только в определенных случаях:

template <typename T>
void foo(T t)
{
   ::bar(t);  // :: just in case there a `bar` in `T` namespace
}

Ответ 2

Ответ на этот вопрос почти невозможен, так как он почти полностью связан с стилем, за исключением того, что если вы считаете, что можете захотеть изменить то, откуда вы собираетесь импортировать некоторые объявления (определения)/определения (определения), в которых Если вы используете их, вы не указываете какую-либо область и не импортируете их с помощью директивы using (для импорта подмножества из пространства имен) или директивы using namespace для импорта всего набора из пространства имен.

В основном директива using используется как директива удобства, но она является мощным способом указания того, какие декларации/определения используются. Мое предпочтение заключается в том, чтобы не указывать область действия и импортировать декларации. Это позволяет легко изменять, если когда-либо они необходимы, при одновременном снижении визуального шума. Кроме того, указание области будет означать, что я буду "заблокирован", откуда получаю декларации (ну, я должен сделать глобальный поиск и заменить, чтобы изменить его).

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

Ответ 3

Считываемый код имеет наименьшее количество шума. Префиксы пространства имен обычно не содержат ничего, кроме шума. Таким образом, базовый уровень - не иметь их вообще.

Пространства имен были введены в С++ главным образом для обработки сторонних материалов из одного элемента управления. Чтобы позволить библиотекам сбрасывать префикс, в то время как клиенты могут использовать короткие имена, применяя использование.

Просто потому, что вы можете иметь одно и то же имя во многих пространствах имен, это не означает, что это тоже хорошая идея. Если в проекте используется какая-либо среда, API платформы, набор библиотек, все, что помещает имя в глобальном, эти имена лучше избегать для других целей. Как с префиксами, так и без них они будут нести накладные расходы.

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

Ответ 4

Рассмотрим следующие случаи.

Публичная библиотека.

Вы пишете экспортируемую библиотеку с публичными заголовками. И вы абсолютно не знаете, в какую среду будут включены ваши заголовки. Например, кто-то может сделать:

namespace std = my_space::std;
#include "your_header"

И все ваши определения будут испорчены, если вы просто используете: std::list<int> и т.д. Поэтому хорошей практикой является добавление :: ко всему глобальному. Таким образом, вы можете быть абсолютно уверены в том, что используете. Конечно, вы можете сделать using (в С++ 11) или typedef - но это неправильный путь в заголовках.


Совлокальные файлы .cc/.cpp.

Внутри вашего собственного кода, который каким-либо образом не подвергается публике, но все же редактируемый не только вами - это хорошая практика, чтобы объявить, что вы собираетесь использовать из-за пределов своего пространства имен. Скажем, ваш проект позволяет использовать несколько векторов - не только std::vector. Затем, где это уместно, вы устанавливаете директиву using:

// some includes

using vector = ::std::vector<int>;  // C++11
typedef ::std::vector<int> vector;  // C++03

namespace my_namespace {
...
}  // namespace my_namespace

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

Другой распространенный случай - это смешение глобальных функций C и кода на С++. Не стоит делать никаких typedef с именами функций. В этой ситуации вам следует добавить функции C с оператором разрешения глобальной области :: - во избежание проблем с компиляцией, когда кто-то реализует функцию с одной и той же сигнатурой в том же пространстве имен.

Не используйте :: для относительных типов и пространств имен - или вы потеряете преимущество использования пространств имен вообще.


Ваш собственный код.

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