Используя директиву vs с использованием декларации swap в С++
Обратитесь к приведенному ниже коду:
#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
, которая объясняет это поведение?
Ответы
Ответ 1
Первый случай - это директива using (using namespace X
), и это означает, что имена из пространства имен X
будут доступны для регулярного поиска в первом общем пространстве имен X
и текущей области. В этом случае первым общим предком пространства имен ::N
и ::std
является ::
, поэтому директива using сделает std::swap
доступной только в том случае, если обратные образы ::
.
Проблема заключается в том, что при запуске поиска он заглянет внутрь функции, затем внутри класса, а затем внутри N
и найдет там ::N::swap
. Поскольку обнаружена потенциальная перегрузка, регулярный поиск не продолжается до внешнего пространства имен ::
. Поскольку ::N::swap
- это функция, компилятор будет выполнять ADL (зависящий от Argument lookup), но набор связанных пространств имен для основных типов пуст, так что это не приведет к какой-либо другой перегрузке. На этом этапе поиск завершается, и начинается перегрузка. Он попытается сопоставить текущую (одну) перегрузку с вызовом и не сможет найти способ преобразования из int
в аргумент ::N::C
, и вы получите ошибку.
С другой стороны, использование объявления (using std::swap
) предоставляет объявление объекта в текущем контексте (в этом случае внутри самой функции). Поиск немедленно найдет std::swap
и прекратит регулярный поиск с помощью ::std::swap
и будет использовать его.
Ответ 2
Очевидная причина заключается в том, что использование декларации и использование
директивы имеют разные эффекты. Использование декларации
вводит имя сразу в текущую область действия, поэтому
using std::swap
вводит имя в локальную область;
поиск останавливается здесь, и единственным символом, который вы найдете, является std::swap
.
Кроме того, это происходит, когда шаблон определен, поэтому позже
объявления в пространстве имен std
не найдены. В следующих
линии, единственным swap
, который будет рассмотрен, является тот, который
определенные в <algorithm>
, плюс те, которые добавлены ADL (таким образом, один
в пространстве имен N
). (Но это правда с VС++? Компилятор
не реализует корректный поиск имени, поэтому кто знает.)
Использование директивы using указывает, что имена будут отображаться "как если бы"
они были объявлены в ближайшем пространстве имен, охватывающих как
директивы и назначенного пространства имен; в вашем случае, глобальные
Пространство имен. И он фактически не вводит имена; Это
просто влияет на поиск имени. Что в случае
символ (или всегда, в случае VС++) имеет место при вызове
сайт.
Что касается того, почему у вас есть это конкретное сообщение об ошибке: возможно, больше
проблема с VС++, поскольку, безусловно, нет не выводимых
контекстов в вашем коде. Но нет никаких оснований ожидать двух
варианты имеют одинаковое поведение, независимо от компилятора.
Ответ 3
Примечание. Я удалил определение swap в пространстве имен 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;
В этом случае он становится эквивалентным локальному определению . И может использоваться без проблем.