2017-02-22 9 views
14

Моей путаницы происходит от «C++ Primer 5-е издания» в раздел 13.3, стр 518.Функция скрытие и использование декларирования в C++

Очень осторожные читатели могут задаться вопросом, почему using декларация внутри swap не скрывает заявления для HasPtr версия swap.

Я попытался прочитать его ссылку, но до сих пор не понял почему. Может ли кто-нибудь объяснить это немного, пожалуйста? Благодарю. Вот пример кода вопроса.

Предполагается, что у класса Foo есть член h, который имеет тип HasPtr.

void swap(HasPtr &lhs, HasPtr &rhs) 
{...} 

void swap(Foo &lhs, Foo &rhs) 
{ 
    using std::swap; 
    swap(lhs.h, rhs.h); 
} 

Почему swap для HasPtr не спрятался, который, кажется, объявлен в внешней области в то время как using std::swap во внутреннем объеме? Благодарю.

+1

потому что 'using' не влияет на это –

ответ

10

Поскольку using std::swap; не означает, что «отныне каждый„своп“следует использовать std::swap», но «приносят все перегруженные swap из std в текущей области».

В этом случае эффект будет таким же, как если бы вы написали using namespace std; внутри функции.

+0

Я понимаю, что в описании' using' вводится 'swap' в' std', но если он работает аналогично объявлению нормальной функции, он должен скрывать имена из космоса, а не просто вводит имена в 'std'. Btw, http://stackoverflow.com/questions/32265938/c-using-declaration-and-function-overload говорит, что 'using'declaration и директива различны, что меня смущает. Благодарю. – Sean

+1

И декларация и директива * отличаются друг от друга, но, как я писал, в этом случае эффект одинаковый. – molbdnilo

+0

Спасибо. Если 'f' были переменными, то это должно быть скрыто, не так ли? Объявления переменной и функции имеют разные политики скрытия? – Sean

0

Эффективное использование C++, третье издание Скотт Мейерс (пункт 24)

Когда компиляторы см вызов поменяться, они ищут правой подкачку Invoke. правила поиска имен C++ гарантировать, что это будет найти какой-либо Т-специфический обмен в глобальной области видимости или в том же пространстве имен, типа Т.

В этом случае и во втором блоке кода, составителям искать для HasPtr swap, и если они их не найдут, они возвращаются к общей версии в std.

0

Объявление использования означает рассмотрение всех перегрузок std :: swap, как если бы они были определены в том же пространстве имен, что и функция. Поскольку декларация использования появляется в теле функции, это временно действует только в пределах этой области.

Он идентичен по существу к следующему:

void swap(HasPtr &lhs, HasPtr &rhs) 
{...} 

void swap(int &lhs, int &rhs) { std::swap(lhs, rhs); } 
void swap(char &lhs, char &rhs) { std::swap(lhs, rhs); } 
// etc. for other std::swap overloads of common types 

void swap(Foo &lhs, Foo &rhs) 
{ 
    swap(lhs.h, rhs.h); 
    // the other overloads of swap go out of scope here. 
} 

Правильные правила перегрузки убедитесь, что первая замена является тот, который будет вызван. Это контрастирует с объявлением локальной переменной с именем «swap», которая будет скрыть первую перегрузку.

6

используя декларациюusing std::swap не скрывает функцию, которые вы объявившая поменять HasPtr и Foo. Он приводит имя swap в пространство имен std в декларативную область. При этом std::swap может принять участие в разрешении перегрузки.

От C++ 11 стандарта на:

7.3.3 использования декларации

1. использование декларации вводит имя в декларативную область, в которой с помощью декларации появляется.

с использованием декларирование:

using typenameнеавтоматическоговложенного именем спецификатор неквалифицированного-ID;
using ::unqualified-id;

Имени элемента, указанное в с использованием декларации- объявляются в декларативной области, в которой использовании декларации появляется. [Примечание: Указано только указанное имя; указывая имя перечисления в декларации с использованием декларации, не объявляет ее счетчики в декларативной области с использованием декларации. -end note] Если в заявке с использованием декларации указан конструктор (3.4.3.1), он неявно объявляет набор конструкторов в классе, в котором появляется с использованием декларации (12.9); в противном случае имя, указанное в с использованием декларации, является синонимом имени какого-либо объекта, объявленного в другом месте.

В вашем случае, у вас есть:

void swap(Foo &lhs, Foo &rhs) 
{ 
    using std::swap; 
    swap(lhs.h, rhs.h); 
} 

using std::swap; декларация вводит имя swap из std пространства имен в декларативной области, которая является органом функции swap(Foo&, Foo&). Имя swap из глобального пространства имен по-прежнему доступно в теле функции.

Если то, что вы разместили, является полнотой функции, тогда вам не требуется декларация using std::swap. Вы можете получить с просто:

void swap(Foo &lhs, Foo &rhs) 
{ 
    swap(lhs.h, rhs.h); 
} 

так swap(HasPtr &lhs, HasPtr &rhs) виден из функции.

Теперь ознакомьтесь с приведенным ниже примером.

struct Bar {}; 

void swap(Bar& lhs, Bar& rhs) 
{ 
} 

struct Foo 
{ 
    int a; 
    Bar b; 
}; 

void swap(Foo& lhs, Foo& rhs) 
{ 
    swap(lhs.a, rhs.a); // A problem 
    swap(lhs.b, rhs.b); 
} 

линия отмечена A problem является проблемой, так как не существует функции с именем swap, который может работать с двумя объектами типа int& в качестве аргумента. Вы можете исправить это с помощью одного из следующих методов:

  1. Использование std::swap явно.

    void swap(Foo& lhs, Foo& rhs) 
    { 
        std::swap(lhs.a, rhs.a); 
        swap(lhs.b, rhs.b); 
    } 
    
  2. Введем std::swap в функции.

    void swap(Foo& lhs, Foo& rhs) 
    { 
        using std::swap; 
        swap(lhs.a, rhs.a); 
        swap(lhs.b, rhs.b); 
    } 
    
+3

«Использование-декларация ничего не скрывает» - это скрывает «swap», объявленный в глобальном пространстве имен, для простого неквалифицированного поиска в этом случае. Однако этот 'swap' все еще найден ADL, потому что связанное пространство имен' HasPtr' является глобальным пространством имен. Если глобальная 'swap' имела параметры, скажем,' int & ', и вы вызывали ее с аргументами типа' int' (которые не имеют связанного пространства имен, поэтому ADL ничего не находит), вы увидите скрытие, введенное использующая декларация (будет вызываться перегрузка 'std', хотя глобальная перегрузка является лучшим совпадением). – bogdan

+0

@bogdan, спасибо за указание ошибки. Я согласен со всем в вашем комментарии. –

+0

@bogdan Мне нравится ваш ответ, но поскольку это комментарий вместо ответа, поэтому ... Я проголосовал за вас. Я пробовал на VS 2015, и использование-декларация скрывает эту функцию, это аргумент 'HasPtr' сохраняет мир. 'HasPtr' определяется в глобальном пространстве имен, и поэтому выполняется поиск глобального пространства имен и происходит' swap' с 'HasPtr'. – Sean

0

На самом деле, для swap HasPtr скрыт в то время как using std::swap во внутреннем объеме, но в этом случае std::swap во внутреннем объеме является такой же, как для swap HasPtr во внешней области.