использование директивы против использования объявления 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 что объясняет такое поведение?

13

Решение

Первый случай — это директива 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 и будет его использовать.

20

Другие решения

Очевидная причина в том, что объявление об использовании и использование
Директива имеет разные эффекты. Декларация об использовании
сразу вводит имя в текущую область видимости, поэтому
using std::swap вводит имя в локальную область;
поиск останавливается здесь, и единственный символ, который вы найдете std::swap,
Кроме того, это происходит, когда шаблон определен, поэтому позже
объявления в пространстве имен std не найдены В следующих
линия, только swap это будет считаться тем
определяется в <algorithm>плюс те, которые добавлены ADL (таким образом, один
в пространстве имен N). (Но так ли это с VC ++? Компилятор
не выполняет поиск имени правильно, так что, кто знает.)

Директива using указывает, что имена будут отображаться так, как если бы они были объявлены в ближайшем пространстве имен, охватывающем оба
директива и назначенное пространство имен; в вашем случае глобальный
Пространство имен. И это на самом деле не вводит имена; Это
просто влияет на поиск имени. Который в случае зависимого
Символ (или всегда, в случае VC ++) происходит при вызове
сайт.

Что касается того, почему у вас есть это конкретное сообщение об ошибке: вероятно, больше
проблема с VC ++, так как, конечно, нет никаких выводимых
контексты в вашем коде. Но нет никаких оснований ожидать, что два
Варианты будут иметь одинаковое поведение, независимо от компилятора.

8

Заметка: Я удалил ваше определение подкачки в пространстве имен 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;

В этом случае он становится эквивалентным местное определение. И может быть использован без проблем.

2
По вопросам рекламы [email protected]