#include 与使用
简单来说:
#include <iostream> // include the header
int main() {
// now you can use stuff declared in that header
std::cout << "Hello world" << std::endl;
// if you are lazy you can "use" things:
using std::cout;
using std::endl;
cout << "Hello World" << endl;
}
你确实不必写using namespace std;!适合这样做的情况如此罕见,而真正造成巨大伤害的情况如此频繁,以至于根据经验,您可以记住:永远不要使用它!详情见这里:Why is “using namespace std;” considered bad practice?。重要的是要意识到完全限定名称 std::cout 和不完全限定名称 cout 之间的区别不仅仅是输入或多或少 5 个字符(请继续阅读...)。
库与命名空间
库和命名空间有什么关系?
标准库将所有内容都放在std 命名空间中。命名空间有助于将事物分开。不同的库可以包含other_namespace::vector,不会与std::vector 混淆,因为我们有命名空间。
非常酷的东西
使用命名空间的一个更深层次的原因是Argument Dependent Lookup。我将尝试用一个简单的例子来解释。假设您正在使用带有一些函数模板的库,该函数模板对您必须提供的类型的对象执行某些操作:
namespace library {
template<typename T>
void do_something(T& a,T& b){
std::cout << "wrong...\n";
std::swap(a,b); // (1)
std::cout << "correct\n";
using std::swap;
swap(a,b); // (2)
}
}
我拿了两个对象并交换了两次。你必须忍受我一秒钟才能理解为什么(1)是错误的,只有(2)是正确的。现在我们有一个库函数模板,要使用它,我们需要一些类型T:
namespace A {
struct foo{};
void swap(foo& a,foo& b) {
std::cout << "A::swap" << "\n";
}
}
想象foo 是这样的,我们知道比std::swap 到swap 更好的方法。实际上foo 是空的,所以对于swap 两个对象我们什么都不做。
让我们回顾一下:标准库附带有std::swap。有人写了一个我们想要使用的库(称为library)。我们希望库代码调用A::swap 而不是std::swap。库作者甚至不知道A::swap 的存在。
连同上面的A和library,这段代码
int main() {
A::foo a,b;
library::do_something(a,b);
}
将打印:
wrong...
correct
A::swap
Live Example。发生了什么?这一行:
std::swap(a,b); // (1)
毫无疑问,请致电std::swap。不是我们想要的。我们希望库代码调用我们的A::swap。
现在这个:
using std::swap;
swap(a,b); // (2)
第一行将名称swap 从std 拉入函数范围。在第二行中,ADL 终于启动了,因为它说的是 swap 而不是 std::swap。简而言之,ADL 是:a 和 b 来自命名空间 A,因此当编译器搜索所有可能的 swaps 时,它也会搜索 A。如果它在A 中找到一个,那么它会调用它(如果它在A 中找不到一个,则仍然有来自std 的swap)。因此只有 (2) 调用了我们的自定义交换。
这仅适用于命名空间。 “很酷的东西”是库作者不需要知道任何关于你的命名空间的事情,但如果它存在,他们的库代码仍然会从你的命名空间调用你的函数。
我应该注意,并非所有代码都是通用库代码。通常你想编写代码,你知道每个细节会发生什么,你想知道调用了哪些函数。通常,您不希望您的代码根据是否包含特定标头而表现不同。因此,大量代码最好使用完全限定的函数调用:std::foo。
结论
我希望我能说服您,命名空间不仅仅是或多或少地键入一些字符。 using namespace std; 懒惰完全忽略了命名空间的意义。另一方面,通过using std::foo; foo(); 将名称拉入范围是
完全没问题并启用 ADL。