Чтение онлайн

на главную - закладки

Жанры

Стандарты программирования на С++. 101 правило и рекомендация

Александреску Андрей

Шрифт:

Эта проблема возникает не только при использовании стандартной библиотеки. В С++ с ней можно столкнуться (и это часто происходит на практике) при использовании типов, определенных в тех же пространствах имен, что и функции (в особенности шаблоны функций или операторы), не связанные с данными типами. Постарайтесь не попадаться в эту ловушку.

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

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

Примечание. Да, стандартная библиотека С++ помещает алгоритмы и другие шаблоны функций, таких как

copy
или
distance
, в то же пространство имен, что и множество типов, таких как
pair
или
vector
. Все они находятся в одном пространстве имен. Это неудачное решение, которое вызывает описанные весьма тонкие и трудно локализуемые проблемы. К счастью, теперь у нас больше опыта и мы знаем, как следует поступать. Не делайте так, как сделано в стандартной библиотеке.

"Оборотная сторона" этого вопроса рассматривается в рекомендации 57.

Ссылки

[Stroustrup00] §10.3.2, §11.2.4 • [Sutter00] §34 • [Sutter02] §39-40

59. Не используйте

using
для пространств имен в заголовочных файлах или перед директивой
#include

Резюме

Директива

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

Вывод: не используйте директивы

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

Обсуждение

Вкратце: вы можете и должны свободно и без ограничений использовать объявления и директивы

using
в своих файлах реализации после директив
#include
. Несмотря на повторяющиеся заявления их противников, объявления и директивы
using
не являются злом и не противоречат цели пространств имен. Они просто делают пространства имен более удобными в использовании.

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

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

Однако директивы и объявления

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

Большинство программистов интуитивно понимают, почему директива

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

Но вот одна распространенная ошибка: многие считают, что использование объявления

using
на уровне пространства имен (например,
using N::Widget;
) вполне безопасно. Однако это не так. Такие объявления, как минимум, опасны, причем более тонким и хитрым способом. Рассмотрим следующий код:

// Фрагмент 1

namespace A {

 int f(double);

}

// Фрагмент 2

namespace B {

 using A::f;

 void g;

}

// Фрагмент 3

namespace A {

 int f(int);

}

// Фрагмент 4

void В::g {

 f(1); // какая перегрузка будет вызвана?

}

Здесь опасность заключается в том, что объявление

using
использует текущий список имен
f
в пространстве имен
А
в тот момент, когда это объявление встречается. Таким образом, какая именно перегрузка будет видима в пространстве имен
В
, зависит от того, где именно находится приведенный код фрагментов и в каком порядке он скомбинирован. (Здесь должен раздаться предупреждающий рев вашей внутренней сирены — "Зависимость от порядка есть зло!") Вторая перегрузка,
f(int)
, в большей степени соответствует вызову
f(1)
, но
f(int)
будет невидима для
B::g
, если ее объявление окажется после объявления
using
.

Рассмотрим два частных случая. Пусть фрагменты 1, 2 и 3 находятся в трех различных заголовочных файлах

s1.h
,
s2.h
и
s3.h
, а фрагмент 4 — в файле реализации
s4.срр
, который включает указанные заголовочные файлы. Тогда семантика
B::g
зависит от порядка, в котором заголовочные файлы включены в
s4.срр
! В частности:

• если

s3.h
идет перед
s2.h
, то
B::g
будет вызывать
A::f(int)
;

Поделиться:
Популярные книги

Черный Маг Императора 6

Герда Александр
6. Черный маг императора
Фантастика:
юмористическое фэнтези
попаданцы
аниме
7.00
рейтинг книги
Черный Маг Императора 6

Оцифрованный. Том 1

Дорничев Дмитрий
1. Линкор Михаил
Фантастика:
боевая фантастика
попаданцы
аниме
5.00
рейтинг книги
Оцифрованный. Том 1

Кодекс Охотника. Книга XIV

Винокуров Юрий
14. Кодекс Охотника
Фантастика:
боевая фантастика
попаданцы
аниме
5.00
рейтинг книги
Кодекс Охотника. Книга XIV

Штуцер и тесак

Дроздов Анатолий Федорович
1. Штуцер и тесак
Фантастика:
боевая фантастика
альтернативная история
8.78
рейтинг книги
Штуцер и тесак

Я снова граф. Книга XI

Дрейк Сириус
11. Дорогой барон!
Фантастика:
боевая фантастика
попаданцы
аниме
5.00
рейтинг книги
Я снова граф. Книга XI

Болотник

Панченко Андрей Алексеевич
1. Болотник
Фантастика:
попаданцы
альтернативная история
6.50
рейтинг книги
Болотник

Кодекс Крови. Книга III

Борзых М.
3. РОС: Кодекс Крови
Фантастика:
фэнтези
попаданцы
аниме
5.00
рейтинг книги
Кодекс Крови. Книга III

Жестокая свадьба

Тоцка Тала
Любовные романы:
современные любовные романы
4.87
рейтинг книги
Жестокая свадьба

Стеллар. Трибут

Прокофьев Роман Юрьевич
2. Стеллар
Фантастика:
боевая фантастика
рпг
8.75
рейтинг книги
Стеллар. Трибут

Голодные игры

Коллинз Сьюзен
1. Голодные игры
Фантастика:
социально-философская фантастика
боевая фантастика
9.48
рейтинг книги
Голодные игры

Последняя Арена 8

Греков Сергей
8. Последняя Арена
Фантастика:
боевая фантастика
рпг
5.00
рейтинг книги
Последняя Арена 8

Черный маг императора 2

Герда Александр
2. Черный маг императора
Фантастика:
юмористическая фантастика
попаданцы
аниме
6.00
рейтинг книги
Черный маг императора 2

Последний Паладин

Саваровский Роман
1. Путь Паладина
Фантастика:
фэнтези
попаданцы
аниме
5.00
рейтинг книги
Последний Паладин

Измена. Свадьба дракона

Белова Екатерина
Любовные романы:
любовно-фантастические романы
эро литература
5.00
рейтинг книги
Измена. Свадьба дракона