在C
中,如果我使用#include "someFile.h"
,预处理器会进行文本导入,这意味着someFile.h
的内容会“复制并粘贴”到#include
行。在C++
中,有using
指令。这是否与#include
类似,即:命名空间的文本导入?
using namespace std; // are the contents of std physically inserted on this line?
如果不是这种情况,那么using
指令是如何实现的。
答案 0 :(得分:2)
不,它没有。这意味着您可以从此行开始使用std
命名空间中没有std::
前缀的类和函数。它不是#include
的替代品。遗憾的是,#include
仍然在C ++中。
示例:
#include <iostream>
int main() {
std::cout << "Hello "; // No `std::` would give compile error!
using namespace std;
cout << "world!\n"; // Now it's okay to use just `cout`.
return 0;
}
答案 1 :(得分:2)
using namespace X
只会告诉编译器“在查找名称时,查看X以及当前名称空间”。它不会“导入”任何东西。有许多不同的方法可以在编译器中实际实现它,但效果是“X中的所有符号看起来好像它们在当前命名空间中可用”。
或者换句话说,在搜索符号时,编译器会在符号前添加X::
(以及在没有命名空间的情况下搜索名称本身)。
[它变得相当复杂,我通常会避免它,如果你有一个符号X::a
和本地值a
,或者你也使用using namespace Y
,还有一个符号Y::a
。我确信C ++标准可以说哪些是使用的,但通过使用这样的结构很容易让自己和其他人混淆。]
通常,我在“所有内容”上使用显式名称空间限定符,因此我很少在自己的代码中使用using namespace ...
。
答案 2 :(得分:1)
using
指令没有将任何内容“导入”到文件中。它所做的就是提供更短的方式来编写命名空间中已存在的符号。例如,如果它是文件的前两行,则通常不会编译:
#include <string>
static const string s("123");
<string>
标头定义std::string
,但string
不是一回事。您尚未将string
定义为类型,因此这是一个错误。
下一个代码片段(位于不同文件的顶部)将编译,因为当您编写using namespace std
时,您告诉编译器string
是可接受的写std::string
的方式:
#include <string>
using namespace std;
static const string s("123");
但是当不通常在文件顶部显示时会编译:
using namespace std;
static const string s("123");
,这也不会:
using namespace std;
static const std::string s("123");
那是因为using namespace
实际上没有定义任何新符号;它需要一些其他代码(例如<string>
标题中的代码)来定义这些符号。
顺便说一下,很多人会明智地告诉你不要在任何代码中写using namespace std
。您可以在C ++中很好地编程,而无需为任何命名空间编写using namespace
。但这是在Why is "using namespace std" considered bad practice?
答案 3 :(得分:1)
不,#include
在C ++中仍然可以完全。
要理解using
,首先需要了解命名空间。这是一种避免大型C项目中发生的符号冲突的方法,例如,很难保证两个第三方库不能定义具有相同名称的函数。原则上每个人都可以选择一个唯一的前缀,但我在实际项目中遇到了非静态C链接器符号的真正问题(我正在看着你,Oracle)。
因此,namespace
允许您对事物进行分组,包括整个库,包括标准库。它既可以避免链接器冲突,也可以避免对您正在使用的函数版本产生歧义。
例如,让我们创建一个几何库:
// geo.hpp
struct vector;
struct matrix;
int transform(matrix const &m, vector &v); // v -> m . v
并使用一些STL标题:
// vector
template <typename T, typename Alloc = std::allocator<T>> vector;
// algorithm
template <typename Input, typename Output, typename Unary>
void transform(Input, Input, Output, Unary);
但是现在,如果我们在同一个程序中使用所有三个头文件,我们有两个类型称为vector
,两个函数称为transform
(ok,一个函数和一个函数模板),它&# 39;很难确定编译器每次都能获得正确的编译器。此外,如果无法猜测,很难告诉编译器我们想要什么。
因此,我们修复所有标题以将其符号放在名称空间中:
// geo.hpp
namespace geo {
struct vector;
struct matrix;
int transform(matrix const &m, vector &v); // v -> m . v
}
并使用一些STL标题:
// vector
namespace std {
template <typename T, typename Alloc = std::allocator<T>> vector;
}
// algorithm
namespace std {
template <typename Input, typename Output, typename Unary>
void transform(Input, Input, Output, Unary);
}
我们的程序可以轻松区分它们:
#include "geo.hpp"
#include <algorithm>
#include <vector>
geo::vector origin = {0,0,0};
typedef std::vector<geo::vector> path;
void transform_path(geo::matrix const &m, path &p) {
std::transform(p.begin(), p.end(), p.begin(),
[&m](geo::vector &v) -> void { geo::transform(m,v); }
);
}
既然您了解了名称空间,您还可以看到名称可以变得很长。因此,为了节省在任何地方输入完全限定名称,using
指令允许您将单个名称或整个名称空间注入当前范围。
例如,我们可以像transform_path
一样替换lambda表达式:
#include <functional>
void transform_path(geo::matrix const &m, path &p) {
using std::transform; // one function
using namespace std::placeholders; // an entire (nested) namespace
transform(p.begin(), p.end(), p.begin(),
std::bind(geo::transform, m, _1));
// this ^ came from the
// placeholders namespace
// ^ note we don't have to qualify std::transform any more
}
并且仅影响该函数范围内的那些符号。如果其他功能选择注入geo::transform
,我们就不会发生冲突。