C++中的函数隐藏和使用声明
Posted
技术标签:
【中文标题】C++中的函数隐藏和使用声明【英文标题】:Function hiding and using-declaration in C++ 【发布时间】:2017-07-12 07:43:26 【问题描述】:我的困惑来自“C++ Primer 5th edition”第 13.3 节,第 518 页。
非常细心的读者可能想知道为什么
swap
中的using
声明没有隐藏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);
为什么HasPtr
的swap
没有隐藏,它似乎在外部范围内声明,而using std::swap
在内部范围内?谢谢。
【问题讨论】:
因为using
对此没有影响
【参考方案1】:
因为using std::swap;
并不意味着“从今以后,每个'交换'都应该使用std::swap
”,而是“将swap
的所有重载从std
带入当前范围”。
在这种情况下,效果与您在函数中写入using namespace std;
相同。
【讨论】:
我理解using
声明在std
中引入了swap
,但如果它的工作方式类似于普通函数声明,它应该隐藏外部空间的名称,而不仅仅是引入名称std
。顺便说一句,***.com/questions/32265938/… 说using
declaration 和指令是不同的,这让我很困惑。谢谢。
声明和指令是不同的,但正如我写的,在这种情况下效果是一样的。
谢谢。如果f
是变量,那么它应该被隐藏,对吧?变量和函数的声明有不同的隐藏策略?
其实我刚试过,在你的例子中,外层空间的f会被隐藏。
@Sean 是的,你是对的。对不起。我不应该在发烧时这样做。【参考方案2】:
using-declaration using std::swap
不会隐藏您声明交换HasPtr
和Foo
的函数。它将名称 swap
从 std
命名空间带到声明性区域。这样,std::swap
就可以参与重载决议。
来自 C++11 标准:
7.3.3 using 声明
1 using-declaration 将名称引入到 using-declaration 出现的声明性区域中。
使用声明:
using typename
optnested-name-specifier unqualified-id ;using ::
unqualified-id ;在 using-declaration 中指定的成员名称在 using-declaration 出现的声明区域中声明。 [ 注意: 只有指定的名字是这样声明的;在 using-declaration 中指定枚举名称不会在 using-declaration 的声明区域中声明其枚举数。 —结束说明 ] 如果一个 using-declaration 命名了一个构造函数(3.4.3.1),它隐式声明了一个类中的一组构造函数,其中 using-声明出现(12.9);否则,在 using-declaration 中指定的名称是其他地方声明的某个实体名称的同义词。
在你的情况下,你有:
void swap(Foo &lhs, Foo &rhs)
using std::swap;
swap(lhs.h, rhs.h);
using std::swap;
声明将来自 std
命名空间的名称 swap
引入声明区域,即 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&
类型的对象作为参数。您可以使用以下方法之一解决此问题:
明确使用std::swap
。
void swap(Foo& lhs, Foo& rhs)
std::swap(lhs.a, rhs.a);
swap(lhs.b, rhs.b);
在函数中引入std::swap
。
void swap(Foo& lhs, Foo& rhs)
using std::swap;
swap(lhs.a, rhs.a);
swap(lhs.b, rhs.b);
【讨论】:
“使用声明不隐藏任何东西” - 它确实隐藏了在全局命名空间中声明的swap
,以便在这种情况下进行简单的非限定查找。但是,ADL 仍然可以找到 swap
,因为 HasPtr
的关联命名空间是全局命名空间。如果全局 swap
具有 int&
的参数,并且您使用 int
类型的参数调用它(没有关联的命名空间,因此 ADL 找不到任何东西),您会看到由using 声明(将调用 std
重载,即使全局重载更匹配)。
@bogdan,感谢您指出错误。我同意你评论中的一切。
@bogdan 我喜欢你的回答,但因为它是评论而不是答案,所以......我投票给你。我在 VS 2015 上试过,using-declaration 确实隐藏了这个功能,它是 HasPtr
的参数拯救了世界。 HasPtr
是在全局命名空间中定义的,因此搜索全局命名空间并找到 swap
和 HasPtr
。
“全局命名空间中的名称交换仍然可以在函数体中访问。”-- 为什么这是真的?我从 c++ 入门 (5th, 6.4.1) 中读到“如果我们在内部范围内声明一个名称,该名称将隐藏在外部范围内声明的该名称的使用。名称不会跨范围重载。"
@ivy,这是真的,但您仍然可以将名称从外部范围带到内部范围以进行重载解析。见ideone.com/tTtUsG。【参考方案3】:
Effective C++ Third Edition by Scott Meyers(第 24 条)
当编译器看到对 swap 的调用时,他们会搜索正确的 swap 以 调用。 C++ 的名称查找规则确保这将找到任何 全局范围内或与类型在同一命名空间中的特定于 T 的交换 T.
在这种情况下,在第二个代码块中,编译器会查找 HasPtr 交换,如果找不到,则会使用 std 中的通用版本。
【讨论】:
【参考方案4】:using 声明意味着考虑所有 std::swap 重载,就好像它们被定义在与函数相同的命名空间中一样。由于 using 声明出现在函数体中,这只是暂时的:在那个范围内。
效果与以下相同:
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”的局部变量相反,后者将隐藏第一个重载。
【讨论】:
【参考方案5】:实际上,HasPtr 的swap
是隐藏的,而using std::swap
在内部范围内,但在这种情况下,内部范围内的std::swap
与HasPtr 在外部范围内的swap
相同。
【讨论】:
以上是关于C++中的函数隐藏和使用声明的主要内容,如果未能解决你的问题,请参考以下文章