Пожалуйста, обратитесь к коду ниже:
#include <algorithm>
namespace N
{
template <typename T>
class C
{
public:
void SwapWith(C & c)
{
using namespace std; // (1)
//using std::swap; // (2)
swap(a, c.a);
}
private:
int a;
};
template <typename T>
void swap(C<T> & c1, C<T> & c2)
{
c1.SwapWith(c2);
}
}
namespace std
{
template<typename T> void swap(N::C<T> & c1, N::C<T> & c2)
{
c1.SwapWith(c2);
}
}
Как написано выше, код не компилируется в Visual Studio 2008/2010. Ошибка:
'void N::swap(N::C<T> &,N::C<T> &)' : could not deduce template argument for 'N::C<T> &' from 'int'.
Однако, если я закомментирую (1) и раскомментирую (2), он скомпилируется нормально. В чем разница между using namespace std
а также using std::swap
что объясняет такое поведение?
Первый случай — это директива using (using namespace X
), и это означает, что имена из пространства имен X
будет доступен для регулярного поиска в первом общем пространстве имен X
и текущая область. В этом случае первый общий предок пространства имен ::N
а также ::std
является ::
так что директива using сделает std::swap
доступно только при поиске ::
,
Проблема здесь в том, что когда поиск начинается, он смотрит внутрь функции, затем в класс, затем внутрь N
и он найдет ::N::swap
там. Поскольку обнаружена потенциальная перегрузка, обычный поиск не продолжается во внешнем пространстве имен ::
, Так как ::N::swap
это функция, которую компилятор будет выполнять ADL (поиск, зависящий от аргумента), но набор связанных пространств имен для фундаментальных типов пуст, так что никакой другой перегрузки не будет. В этот момент поиск завершается, и начинается разрешение перегрузки. Он попытается сопоставить текущую (одиночную) перегрузку с вызовом и не сможет найти способ преобразования из int
к аргументу ::N::C
и вы получите ошибку.
С другой стороны, объявление об использовании (using std::swap
) обеспечивает объявление сущности в текущем контексте (в данном случае внутри самой функции). Поиск найдет std::swap
немедленно и прекратить регулярный поиск с ::std::swap
и будет его использовать.
Очевидная причина в том, что объявление об использовании и использование
Директива имеет разные эффекты. Декларация об использовании
сразу вводит имя в текущую область видимости, поэтому
using std::swap
вводит имя в локальную область;
поиск останавливается здесь, и единственный символ, который вы найдете std::swap
,
Кроме того, это происходит, когда шаблон определен, поэтому позже
объявления в пространстве имен std
не найдены В следующих
линия, только swap
это будет считаться тем
определяется в <algorithm>
плюс те, которые добавлены ADL (таким образом, один
в пространстве имен N
). (Но так ли это с VC ++? Компилятор
не выполняет поиск имени правильно, так что, кто знает.)
Директива using указывает, что имена будут отображаться так, как если бы они были объявлены в ближайшем пространстве имен, охватывающем оба
директива и назначенное пространство имен; в вашем случае глобальный
Пространство имен. И это на самом деле не вводит имена; Это
просто влияет на поиск имени. Который в случае зависимого
Символ (или всегда, в случае VC ++) происходит при вызове
сайт.
Что касается того, почему у вас есть это конкретное сообщение об ошибке: вероятно, больше
проблема с VC ++, так как, конечно, нет никаких выводимых
контексты в вашем коде. Но нет никаких оснований ожидать, что два
Варианты будут иметь одинаковое поведение, независимо от компилятора.
Заметка: Я удалил ваше определение подкачки в пространстве имен std. Это не актуально здесь. Даже без него код будет иметь те же проблемы.
Это связано с поиском различий между правилами using directive
(using namespace std
) и using
declaration
(using std::swap
)
Microsoft говорит
Если локальная переменная
имеет то же имя, что и переменная пространства имен, переменная пространства имен
скрытый. Ошибка иметь переменную пространства имен с тем же именем
как глобальная переменная.
#include<iostream>
namespace T {
void flunk(int) { std::cout << "T";}
}
namespace V {
void flunk(int) { std::cout << "V";}
}int main() {
using T::flunk; // makes T::flunk local
// using V::flunk; // makes V::flunk local. This will be an error
using namespace V; // V::flunk will be hidden
flunk(1);
}
Согласно этому, из-за вашего
template <typename T>
void swap(C<T> & c1, C<T> & c2)
std::swap
будет скрыт при использовании
using namespace std;
Так что единственный swap
доступно для шаблона вычитание N::swap
и это не будет работать для int
с, потому что он ожидает template class
в качестве аргумента.
но не когда
using std::swap;
В этом случае он становится эквивалентным местное определение. И может быть использован без проблем.