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

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

Жанры

Стандарты программирования на С++. 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 находил имена из неподходящего пространства имен просто потому, что типы из этих пространств имен использовались поблизости друг от друга.

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

Газлайтер. Том 8

Володин Григорий
8. История Телепата
Фантастика:
попаданцы
альтернативная история
аниме
5.00
рейтинг книги
Газлайтер. Том 8

На Ларэде

Кронос Александр
3. Лэрн
Фантастика:
фэнтези
героическая фантастика
стимпанк
5.00
рейтинг книги
На Ларэде

Охота на попаданку. Бракованная жена

Герр Ольга
Любовные романы:
любовно-фантастические романы
5.60
рейтинг книги
Охота на попаданку. Бракованная жена

Кай из рода красных драконов

Бэд Кристиан
1. Красная кость
Фантастика:
попаданцы
альтернативная история
5.00
рейтинг книги
Кай из рода красных драконов

Хозяйка Проклятой Пустоши. Книга 2

Белецкая Наталья
2. Хозяйка Проклятой Пустоши
Любовные романы:
любовно-фантастические романы
5.00
рейтинг книги
Хозяйка Проклятой Пустоши. Книга 2

Безумный Макс. Поручик Империи

Ланцов Михаил Алексеевич
1. Безумный Макс
Фантастика:
героическая фантастика
альтернативная история
7.64
рейтинг книги
Безумный Макс. Поручик Империи

Потусторонний. Книга 2

Погуляй Юрий Александрович
2. Господин Артемьев
Фантастика:
фэнтези
попаданцы
аниме
5.00
рейтинг книги
Потусторонний. Книга 2

Чапаев и пустота

Пелевин Виктор Олегович
Проза:
современная проза
8.39
рейтинг книги
Чапаев и пустота

Солнечный корт

Сакавич Нора
4. Все ради игры
Фантастика:
зарубежная фантастика
5.00
рейтинг книги
Солнечный корт

Лютая

Шёпот Светлана Богдановна
Любовные романы:
любовно-фантастические романы
6.40
рейтинг книги
Лютая

Ведьмак (большой сборник)

Сапковский Анджей
Ведьмак
Фантастика:
фэнтези
9.29
рейтинг книги
Ведьмак (большой сборник)

Наследие Маозари 4

Панежин Евгений
4. Наследие Маозари
Фантастика:
фэнтези
попаданцы
5.00
рейтинг книги
Наследие Маозари 4

Ученик

Губарев Алексей
1. Тай Фун
Фантастика:
фэнтези
5.00
рейтинг книги
Ученик

Начальник милиции. Книга 5

Дамиров Рафаэль
5. Начальник милиции
Фантастика:
попаданцы
альтернативная история
5.00
рейтинг книги
Начальник милиции. Книга 5