我对Koenig和Moo的 Accelerated C ++ 一书中的以下句子感到困惑:
// median.h
#include <vector>
double median(std::vector<double>);
在此示例中,我们使用
std::vector
的限定名称,因为 我们无法知道函数用户希望如何引用 到std::vector
。我们代码的用户可能不需要using
-vector
的声明。如果我们在标头中写一个 包含头文件的所有程序都将获得using std::vector
声明,无论他们是否想要它。头文件应该 使用标准名称而不是using
-声明。
我不认为我理解这段话。如果我们在标头中有一个using std::vector
声明,并且包括标头的整个程序都得到了相同的声明,那么仅当用户重新定义vector
类时,这才可能成为问题,不是吗?但是我们的median
函数仍然需要标准库中的vector
,因此,如果用户想在新定义的median
上使用vector
,则会产生错误在编译时。
答案 0 :(得分:6)
如果头文件是:
#include <vector>
using std::vector;
double median(vector<double>);
这将告诉所有包含median.h
的编译单元(例如cpp文件)函数median
接受std::vector
,同时使std::vector
可以通过无限定的查询访问vector
在这些编译单元中。
如果编译单元可以访问另一个不合格的vector
,则将发生名称冲突和构建错误。函数median
永远不会收到不是std::vector
的类型,即使该类型的名称也是vector
。
避免在头文件中声明using
是个好主意,因为这是造成名称冲突的原因。
答案 1 :(得分:4)
要理解这些单词,有必要了解像using
这样的using namespace std
指令的作用。
默认情况下,当编译器看到vector
之类的名称时,它会在当前上下文中查找vector
(即,在您的示例中,它在“全局名称空间”中,或者等效地在{ {1}})。如果在该搜索中未找到候选匹配项(例如,名为::
的匹配项),则会发出诊断信息,并停止编译。
vector
使编译器在命名空间using namespace std
中查找匹配的名称。如果找到明确的匹配项,则使用该名称。
如果存在另一个std
指令(例如using
),则会出现问题。例如;
using namespace foo
现在考虑如果 using namespace std;
#include "foo.h"
using namespace foo;
#include "median.h"
在命名空间foo.h
中指定一个模板化名称为vector
的东西会发生什么。假设foo
有
median.h
由于有两个double median(vector<double>);
指令,因此编译器会在using
和std
中查找名称foo
。如果它在两个名称空间中都找到一个匹配项,则它具有两个候选匹配名称vector
和foo::vector
。
如果这两个名称都与std::vector
完全相同,则由于含糊不清,因此该代码具有可诊断的错误。编译将失败。没有规则使编译器认为名称空间vector
中的匹配比名称空间std
中的匹配要好,反之亦然。
下一个问题是无法撤消foo
指令的影响(例如,与其他using
指令或声明一起使用)。在这种情况下编译代码的唯一方法是将using
的声明更改为
median()
Koenig和Moo并没有从头开始,而是从一开始就提倡使用全名double median(std::vector<double>);
,而不是在标头中使用std::vector<double>
指令。这样做可以使使用头文件的其他程序员在愿意的情况下使用using
指令或声明,但并不要求这样做。
注意:以上内容过于简化。有很多详细的规则可以控制编译器如何查找候选名称,然后确定它们的匹配程度。