Почему (не относящееся к делу) использование объявления может примирить неоднозначность перегрузки с зависящим от аргумента поиском?
Это продолжение вопроса здесь о перегрузке функций с зависящим от аргумента поиском (ADL). Я хотел проверить свое понимание правил в этих обстоятельствах, поэтому я написал тестовый код.
Во-первых, конечно, в std нет swap для класса HasPtr, поэтому я написал собственное пространство имен, которое содержит версию hasptr swap в дополнение к уже определенной в глобальной области видимости. Объявление using работает так, как я и ожидал-ошибка двусмысленности была произведено потому, что существует версия hasptr swap, уже определенная, как это делается в "C++ Primer", 5ed.Затем я хочу посмотреть, что произойдет, если я изменю using declaration на using directive. В книге говорится, что компилятор будет молчать, пока функция не будет вызвана на самом деле. Я хочу проверить это, поэтому код выглядит следующим образом:
#include <string>
class HasPtr {
public:
friend void swap(HasPtr&, HasPtr&);
std::string *ps;
};
void swap(HasPtr &lhs, HasPtr &rhs) {
swap(lhs.ps, rhs.ps); // swap the pointers, not the string data
}
namespace myNS {
void swap(HasPtr &lhs, HasPtr &rhs) {
std::string s = "in my name space";
swap(lhs.ps, rhs.ps); // swap the pointers, not the string data
}
}
class Foo {
friend void swap(Foo &lhs, Foo &rhs);
HasPtr h;
};
void swap(Foo &lhs, Foo &rhs) {
using std::swap; //<- commenting this line will cause error
using namespace myNS;
swap(lhs.h, rhs.h);
}
int main() {
Foo f1, f2;
swap(f1, f2);
}
Странная вещь произошла в строке 27 (using std::swap;
). Если я это прокомментировал, то имя myNS:: swap, которое имеет точно такую же подпись, как и то, которое уже определено в глобальный охват был поднят до глобального масштаба и, как я и ожидал, вызвал ошибку перегруженности двусмысленностью.
Но, если я не комментирую строку 27 и не компилирую, никакой ошибки двусмысленности не сообщается. И программа выполняет:: swap, первоначально определенный в глобальной области видимости, как будто директива using using namespace myNS;
не поднимает myNS:: swap так, чтобы он не был добавлен в набор кандидатов для перегрузки. Я просто не мог понять этот феномен. Почему можно используя объявления от несущественных имен (std, конечно, не содержит версию hasptr swap) примирить неоднозначность перегрузки под ADL? Почему именно оригинал:: swap, а не его конкурент в myNS, выбран для выполнения? Имеет ли строка 27 какие-либо побочные эффекты к перегрузке процесса (скажем, подавление имен из поднятого пространства имен, чтобы исходные имена имели более высокий приоритет)? Спасибо за ваши ответы.
Проблема может быть воспроизведена в Visual Studio 2015 Update 3 на Windows 7 и в GCC 4.8.4 на ubuntu 14.04, оба 64-битовый.
2 ответа:
Механика в игре здесь троякая.
A используя объявление, как
using std::swap
, является декларацией . Он вводит объявлениеswap
в декларативную область функции.С другой стороны, директива using не вводит объявления в текущую декларативную область. Он позволяет только безоговорочному поиску обрабатывать имена из назначенных пространств имен, , как если бы они были объявлены в ближайшем заключительном пространстве имен текущей декларативной области.
Объявления в более мелких декларативных областях скрывают объявления из более крупных включающих декларативных областей.
По отношению к вышесказанному, способ его настройки выглядит следующим образом:
std::swap
объявляется внутриswap(Foo, Foo)
.- имена внутри
myNS
становятся доступными дляswap(Foo, Foo)
, как если бы они были объявлены вместе с ним в одном пространстве имен.- в объявление, добавленное в #1, скрывает сделанное видимым в #2.
::swap
может быть найден ADL (несмотря на то, что он также скрыт #1), ноmyNS::swap
не может. поскольку версияmyNS
и скрыта, и не найдена ADL, она ни с чем не конфликтует.Когда вы удаляете объявление
std::swap
, теперь у вас естьmyNS::swap
видимый. ADL также находит::swap
, давая вам две перегрузки. Они оба являются допустимыми перегрузками и порождают очевидную двусмысленность.
Обратите внимание, что using-directive и using-declaration имеют разный эффект:
(Курсив мой)
Using-directive: с точки зрения безусловного поиска имени любого имени после директивы using и до конца области, в которой оно появляется, каждое имя из ns_name является видимым , как если бы оно было объявлено в ближайшем заключающем пространстве имен, которое содержит как директиву using, так и ns_name.
Это означает для
using namespace myNS;
, имяmyNS::swap
видно так, как если бы оно было объявлено в глобальной области видимости. Еслиusing std::swap;
прокомментировано, то 2swap
s будут найдены в глобальной области видимости и затем вызовут неоднозначность.Если
using std::swap;
раскомментирован, то пространство именswap
изstd
будет найдено в области действия функции, тогда поиск имени остановится, дальнейшая глобальная область не будет проверяться. Заметим, что глобальные::swap
могут быть найдены ADL, с добавлениемstd::swap
они будут рассмотрены в разрешении перегрузки, и::swap
является выбран тогда без двусмысленности.myNS::swap
не будет работать в этом случае.Поиск имени исследует области, как описано ниже, пока не найдет по крайней мере одно объявление любого вида, после чего поиск останавливается и никакие другие области не рассматриваются.