У меня есть два фрагмента для ADL в демонстрационных целях. Оба фрагмента были скомпилированы компиляторами VC10, gcc и comeau C ++, и результат одинаков для всех трех.
‹1> ADL против использования директивы пользовательского пространства имен:
#include <algorithm>
namespace N
{
struct T {};
void swap(T,T) {}
}
namespace M
{
void swap(N::T,N::T) {}
}
int main()
{
using M::swap;
N::T o1,o2;
swap(o1,o2);
}
Результат компиляции:
error C2668: 'M::swap' : ambiguous call to overloaded function
could be 'void M::swap(N::T,N::T)'
or 'void N::swap(N::T,N::T)' [found using argument-dependent lookup]
Это ожидается, поскольку ADL не имеет приоритета над обычным результатом поиска, плюс ADL не является гражданином 2-го класса, результат поиска ADL объединяется с обычным (не ADL) неквалифицированным поиском. Вот почему у нас есть двусмысленность.
‹2> ADL против использования директивы пространства имен std:
#include <algorithm>
namespace N
{
struct T {};
void swap(T,T) {} //point 1
}
namespace M
{
void swap(N::T,N::T) {}
}
int main()
{
using std::swap;
N::T o1,o2;
swap(o1,o2);
}
Этот компилируется нормально.
В результате компилятор выбирает результат ADL (имеет прецедент std :: swap), что означает, что будет вызываться N::swap()
в «точке 1». Только когда в отсутствие «точки 1» (скажем, если я закомментирую эту строку), компиляция вместо этого будет использовать откат std::swap
.
Обратите внимание, что этот способ использовался во многих местах как способ перезаписать std::swap
. Но у меня вопрос: почему ADL имеет приоритет над «пространством имен std» (case2), но считается равным определяемой пользователем функции пространства имен (case1)?
Есть ли в стандарте C ++ абзац, в котором говорится об этом?
================================================== =============================== Редактировать после прочтения полезных ответов, может быть полезно другим.
Итак, я настроил свой фрагмент 1, и теперь двусмысленность исчезла, и при компиляции явно предпочитаю функцию Nontemplate при разрешении перегрузки!
#include <algorithm>
namespace N
{
struct T {};
void swap(T,T) {}
}
namespace M
{
template<class T>
void swap(N::T,N::T) {}
}
int main()
{
using M::swap;
N::T o1,o2;
swap(o1,o2); //here compiler choose N::swap()
}
Я также изменил свой фрагмент 2. Просто для развлечения!
#include <algorithm>
namespace N
{
struct T {};
template<class _Ty> inline
void swap(_Ty& _Left, _Ty& _Right)
{
_Ty _Tmp = _Move(_Left);
_Left = _Move(_Right);
_Right = _Move(_Tmp);
}
}
namespace M
{
void swap(N::T,N::T) {}
}
int main()
{
using std::swap;
N::T o1,o2;
swap(o1,o2);
}
gcc и comeau, как и ожидалось, говорят о двусмысленности:
"std::swap" matches the argument list, the choices that match are:
function template "void N::swap(_Ty &, _Ty &)"
function template "void std::swap(_Tp &, _Tp &)"
Кстати, VC10, как обычно, глупо, пусть это пройдет нормально, если я не удалю 'using std :: swap'.
Еще немного, чтобы написать: перегрузка C ++ может быть сложной (30+ страниц в стандарте C ++), но в приложении B есть очень удобочитаемая страница из 10 ...
Спасибо за хороший вклад, теперь все ясно.