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

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

Жанры

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

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

Шрифт:

Язык С++ спроектирован с явным учетом Принципа Интерфейса. Причина, по которой в язык добавлен поиск, зависящий от аргумента (argument-dependent lookup — ADL), известный также как поиск Кёнига, заключается в том, чтобы обеспечить коду, использующему объект

x
типа
X
, возможность работать с частью его интерфейса, состоящей из функций, не являющихся членами (например, инструкция
cout << x
использует оператор
operator<<
, который не является членом класса
X
) так же легко, как и функции-члены (например, вызов
x.f
) не требует выполнения специального поиска, поскольку очевидно, что поиск
f
выполняется в области видимости
X
). ADL обеспечивает
для свободных функций, которые получают объект
X
в качестве аргумента и поставляются вместе с определением
X
ту же простоту использования, что и для функций-членов интерфейса
X
. Одним из главных мотивов принятия ADL был, в частности, класс
std::string
(см. [Sutter00]).

Рассмотрим класс

X
, определенный в пространстве имен N:

class X {

publiс:

 void f;

};

X operator+(const X&, const X&);

В вызывающей функции обычно пишется код наподобие

x3=x1+x2
, где
x1
,
x2
и
x3
— объекты типа
X
. Если оператор
operator+
объявлен в том же пространстве имен, что и
X
, никаких проблем не возникает, и такой код отлично работает, поскольку оператор
operator+
будет легко найден с помощью ADL.

Если же оператор

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

x3 = N::operator+(x1, x2);

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

using
:

using N::operator+;

// или: using namespace N;

x3 = x1 + x2;

Применение

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

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

Примеры

Пример 1. Операторы. Операторы работы с потоками

operator<<
и
operator>>
для объектов некоторого класса
X
, вероятно, относятся к наиболее ярким примерам функций, которые вполне очевидно являются частью интерфейса класса
X
, но при этом всегда представляют собой свободные функции (это обязательное условие, поскольку левый аргумент этих операторов — поток, а не объект
X
). Та же аргументация применима и к другим операторам, не являющимся членами
X
. Убедитесь, что ваши операторы находятся в том же пространстве имен, что и класс, с которым они работают. Если у вас есть возможность выбора, лучше делать операторы и все прочие функции не членами и не друзьями класса (см. рекомендацию 44).

Пример 2. Прочие функции. Если автор

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

Ссылки

[Stroustrup00]

§8.2, §10.3.2, §11.2.4 • [Sutter00] §31-34

58. Храните типы и функции в разных пространствах имен, если только они не предназначены для совместной работы

Резюме

Оберегайте ваши типы от непреднамеренного поиска, зависящего от аргументов (argument-dependent lookup — ADL, известный также как поиск Кёнига); однако преднамеренный поиск должен завершаться успешно. Этого можно добиться путем размещения типов в своих собственных пространствах имен (вместе с непосредственно связанными с ними свободными функциями; см. рекомендацию 57). Избегайте помещения типов в те же пространства имен, что и шаблоны функций или операторов).

Обсуждение

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

Вот реальный пример, который был опубликован в группе новостей:

#include <vector>

namespace N {

struct X { };

template<typename T>

int* operator+(T , unsigned) {/* некоторые действия */}

}

int main {

 std::vector<N::X> v(5);

 v[0];

}

Инструкция

v[0];
компилируется в некоторых реализациях стандартной библиотеки, но не во всех. Попробуем кратко пересказать эту длинную историю. Очень тонкая проблема связана с тем, что внутри большинства реализаций
vector<T>::operator[]
спрятан код наподобие
v.begin+n
, и поиск имен для функции
operator+
может достичь пространства имен (в нашем случае
N
) типа, для которого инстанцирован вектор (в нашем случае
X
). Достигнет ли поиск этого пространства имен или нет — зависит от того, как определен
vector<T>::iterator
в данной версии реализации стандартной библиотеки. Однако если поиск достигает
N
, то здесь он находит
N::operator+
. Наконец, в зависимости от используемых типов, компилятор может просто посчитать, что для
vector<T>::iterator
оператор
N::operator+
имеет лучшее соответствие, чем оператор
std::operator+
из реализации стандартной библиотеки (который и должен был быть вызван). (Один из способов избежать такой неприятности в реализации стандартной библиотеки — не использовать код
v.begin+n
таким образом, что он вносит непреднамеренную точку настройки: либо надо изменить код так, чтобы тип
v.begin
никаким образом не зависел от параметра шаблона, либо вызов
operator+
следует переписать с указанием полного квалифицированного имени. См. рекомендацию 65.)

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

N::operator+
окажется, к несчастью, вполне подходящим с точки зрения компилятора, и программа скомпилируется успешно, но вот результаты ее работы могут оказаться совершенно неожиданными...

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

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

Черный Маг Императора 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
рейтинг книги
Измена. Свадьба дракона