2013-04-22 10 views
12

Пожалуйста, обратитесь к ниже код:с помощью директивы против использования декларации своп в C++

#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), он будет компилировать OK. В чем разница между using namespace std и using std::swap, которая объясняет это поведение?

+4

Это похоже на проблему с областью. Правило (если я не ошибаюсь), он всегда будет использовать самую локальную область. Поэтому он будет использовать 'N :: swap' вместо' std :: swap', даже если у вас есть 'using namespace std' – andre

+10

Btw, код плохо сформирован, а программа имеет Undefined Behavior. Вы не можете добавить функциональные шаблоны * перегрузки * в пространство имен 'std', только специализации. –

+0

Также, как это своп предположительно работает, насколько я могу судить, это бесконечный цикл. – andre

ответ

18

В первом случае с помощью директивы (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

Примечание: Я удалил определение swap в пространстве имен std. Здесь это не актуально. Даже без этого код будет иметь те же проблемы.


Это связано искать правила различия между using directive (using namespace std) и usingdeclaration (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 s, потому что он ожидает аргумент template class.

но не когда

using std::swap; 

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

+1

В опубликованном сообщении об ошибке не упоминается такая двусмысленность. Понятно, что речь идет только об одной «свопе». –

+1

Пока компилятор может видеть 'swap' в' ', его код должен компилироваться. Остальные двое подпадают под категорию SFINAE и просто не рассматриваются (в случае, когда они видны). –

+1

@JamesKanze Да, но в соответствии с правилами поиска выше нет другого свопа, который будет использоваться для SFINAE в первую очередь. Существует ** только один ** видимый своп. – stardust

8

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

с помощью директивы указывает, что имена будут отображаться «как если бы» они были объявлены в ближайшем пространстве имен объемлющего как директива , так и номинированное пространство имен; в вашем случае, глобальное пространство имен . И он фактически не вводит имена; он просто влияет на поиск имени. Что в случае зависимого символа (или всегда, в случае VC++) имеет место на сайте .

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

+1

+1. * Это правда в VS? * Реализация неверна в том, что она также добавит объявления, найденные между определением шаблона и точкой создания экземпляра (так же, как и многие версии gcc и CC), но кроме этого он найдет правильные один в этом случае (есть много других случаев, когда это не будет :) –

+1

@ DavidRodríguez-dribeas MS реализует версию поиска имен, которая была распространена _before_ C++ 98. Они, очевидно, адаптировали его с тех пор, поскольку исходная версия была определена до пространств имен. Как «использование namespace x;» и «using x;» интерпретируются в этой версии, это чья-то догадка. (И не хватает ли 20 лет для внедрения стандарта?) –

+1

@JamesKanze MSVC не реализует поиск двухфазных имен правильно, задерживая поиск не зависимых имен на второй фазе. См. это [вопрос] (http://stackoverflow.com/questions/6273176/what-exactly-is-broken-with-microsoft-visual-cs-two-phase-template-instanti) Этот вопрос не должен вступать в игру здесь AFAICS , – TemplateRex

 Смежные вопросы

  • Нет связанных вопросов^_^