使用指令C ++实现

时间:2014-05-10 20:07:59

标签: c++ c using-directives

C中,如果我使用#include "someFile.h",预处理器会进行文本导入,这意味着someFile.h的内容会“复制并粘贴”到#include行。在C++中,有using指令。这是否与#include类似,即:命名空间的文本导入?

using namespace std; // are the contents of std physically inserted on this line?

如果不是这种情况,那么using指令是如何实现的。

4 个答案:

答案 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,我们就不会发生冲突。