Функция скрытия и использования-объявления в С++
Моя путаница происходит из раздела "С++ Primer 5th edition" 13.3, стр. 518.
Очень осторожные читатели могут задаться вопросом, почему декларация using
внутри swap
не скрывает объявления для HasPtr
версии swap
.
Я попытался прочитать его ссылку, но до сих пор не понял почему. Может ли кто-нибудь объяснить это немного, пожалуйста? Благодарю. Вот пример кода вопроса.
Предположим, что класс Foo
имеет член с именем h
, который имеет тип HasPtr
.
void swap(HasPtr &lhs, HasPtr &rhs)
{...}
void swap(Foo &lhs, Foo &rhs)
{
using std::swap;
swap(lhs.h, rhs.h);
}
Почему swap
для HasPtr
не скрыт, который, кажется, объявлен во внешней области, а using std::swap
находится во внутренней области? Спасибо.
Ответы
Ответ 1
Потому что using std::swap;
не означает "отныне, каждый" обмен "должен использовать std::swap
", но "переносит все перегрузки swap
из std
в текущую область действия".
В этом случае эффект такой же, как если бы вы написали using namespace std;
внутри функции.
Ответ 2
Объявление-использование using std::swap
не скрывает функции, которые вы объявили, для обмена HasPtr
и Foo
. Это имя swap
формирует пространство имен std
в декларативной области. При этом std::swap
может принимать участие в разрешении перегрузки.
Из стандарта С++ 11:
7.3.3 Объявление использования
1 Использование-декларация вводит имя в декларативный регион, в котором появляется декларация использования.
using-declaration:
using typename
opt идентификатор nested-name-unqualified-id;
using ::
unqualified-id;
Имя участника, указанное в объявлении using, объявляется в декларативной области, в которой появляется декларация использования. [Примечание: объявлено только указанное имя; указание имени перечисления в декларации использования не объявляет его счетчики в декларативной области using-declarations. -end note] Если декларация using называет конструктор (3.4.3.1), он неявно объявляет набор конструкторов в классе, в котором появляется декларация использования (12.9); в противном случае имя, указанное в объявлении using, является синонимом имени какого-либо объекта, объявленного в другом месте.
В вашем случае у вас есть:
void swap(Foo &lhs, Foo &rhs)
{
using std::swap;
swap(lhs.h, rhs.h);
}
Объявление using std::swap;
вводит имя swap
из пространства имен std
в декларативную область, которая является телом функции swap(Foo&, Foo&)
. Имя swap
из глобального пространства имен все еще доступно в теле функции.
Если то, что вы разместили, является полнотой функции, тогда вам не нужно объявление using std::swap
. Вы могли бы просто:
void swap(Foo &lhs, Foo &rhs)
{
swap(lhs.h, rhs.h);
}
так как swap(HasPtr &lhs, HasPtr &rhs)
видно из функции.
Теперь взглянем на пример ниже.
struct Bar {};
void swap(Bar& lhs, Bar& rhs)
{
}
struct Foo
{
int a;
Bar b;
};
void swap(Foo& lhs, Foo& rhs)
{
swap(lhs.a, rhs.a); // A problem
swap(lhs.b, rhs.b);
}
Линия, помеченная A problem
, является проблемой, так как нет функции с именем swap
, которая может работать с двумя объектами типа int&
в качестве аргумента. Вы можете исправить это, используя один из следующих способов:
-
Использовать std::swap
явно.
void swap(Foo& lhs, Foo& rhs)
{
std::swap(lhs.a, rhs.a);
swap(lhs.b, rhs.b);
}
-
Ввести функцию std::swap
в функцию.
void swap(Foo& lhs, Foo& rhs)
{
using std::swap;
swap(lhs.a, rhs.a);
swap(lhs.b, rhs.b);
}
Ответ 3
Эффективное С++ Третье издание Скотта Мейерса (Пункт 24)
Когда компиляторы видят вызов для свопинга, они ищут правильный своп вызов. Правила поиска имен в С++ гарантируют, что это найдет T-специфический обмен в глобальном масштабе или в том же пространстве имен, что и тип Т.
В этом случае и во втором блоке кода компиляторы ищут смену HasPtr, и если они их не найдут, они возвращаются к общей версии в std.
Ответ 4
Объявление использования означает рассмотрение всех перегрузок std:: swap, как если бы они были определены в том же пространстве имен, что и функция. Поскольку декларация использования появляется в теле функции, это временно действует только в пределах этой области.
Он идентичен по существу следующим образом:
void swap(HasPtr &lhs, HasPtr &rhs)
{...}
void swap(int &lhs, int &rhs) { std::swap(lhs, rhs); }
void swap(char &lhs, char &rhs) { std::swap(lhs, rhs); }
// etc. for other std::swap overloads of common types
void swap(Foo &lhs, Foo &rhs)
{
swap(lhs.h, rhs.h);
// the other overloads of swap go out of scope here.
}
Регулярные правила перегрузки гарантируют, что первая своп - это тот, который вызывается. Это контрастирует с объявлением локальной переменной с именем "swap", которая будет скрывать первую перегрузку.
Ответ 5
Собственно, swap
для HasPtr скрыт во время using std::swap
во внутренней области, но в этом случае std::swap
во внутренней области совпадает с swap
для HasPtr во внешней области.