【问题标题】:What's the scope of the "using" declaration in C++?C++ 中“使用”声明的范围是什么?
【发布时间】:2010-09-18 09:23:35
【问题描述】:

我在 C++ 中使用 'using' 声明将 std::string 和 std::vector 添加到本地命名空间(以节省键入不必要的 'std::'s)。

using std::string;
using std::vector;

class Foo { /*...*/ };

此声明的范围是什么?如果我在标头中执行此操作,它会将这些“使用”声明注入到包含标头的每个 cpp 文件中吗?

【问题讨论】:

标签: c++


【解决方案1】:

头文件没有什么特别之处可以阻止using 声明。这是一个在编译开始之前的简单文本替换。

您可以将using 声明限制在一个范围内:

void myFunction()
{
   using namespace std; // only applies to the function's scope
   vector<int> myVector;
}

【讨论】:

  • 我从没想过我可以在函数中使用它!
  • 我有一堆名称空间都被一个“conglomerator”类型的文件使用,而 gmock 单元测试非常乏味,因为每个测试都使用来自特定名称空间的东西,我想我必须限定每个变量。在函数中使用 using(甚至是 gtest TEST 宏!)让我的生活变得更好!
【解决方案2】:

当您在 C++ 中#include 一个头文件时,它会将头文件的全部内容放入您在源文件中包含它的位置。因此,包含具有 using 声明的文件与将 using 声明置于包含该头文件的每个文件的顶部具有完全相同的效果。

【讨论】:

  • ... 这通常是一件坏事。
  • 但是如果你将using 声明放在namespace 中,它会被限制在该命名空间的范围内,所以通常是可以的(通常需要注意你的特定需求和风格)。跨度>
  • ...但是如果您确实将 using 放在命名空间内,请确保您没有这样做以试图绕过通常是一个坏主意的事情,例如您不能封装类方法在命名空间 Y 之外声明在另一个命名空间 X 内,这样您就可以在本地使用命名空间 X。这就是我们首先使用命名空间 :: 解析器的原因。如果打字有那么大的问题,要么是宏(很容易导致代码异味),要么更好,将其隔离到它自己的源 .cpp 中,您将只在其中使用命名空间。
  • 虽然这个和类似的答案是很好的建议,但他们并没有回答这个问题。
【解决方案3】:

using 语句的范围取决于它在代码中的位置:

  • 位于文件顶部,具有整个文件的范围。
  • 如果这是一个头文件,它将在包含该头文件的所有文件中具有作用域。一般来说,这是“不是一个好主意”,因为它可能会产生意想不到的副作用
  • 否则,using 语句的范围在包含它的块内,从它出现的点到块的末尾。如果它被放置在一个方法中,它将在该方法中具有范围。如果它被放置在一个类定义中,它将在该类中具有范围。

【讨论】:

  • 我认为不可能在类范围内添加using 语句...?因为我想避免在整个地方输入std::,所以我和OP有同样的问题。我的类使用了很多带有智能指针的向量,而五个字符 std:: 前缀增加了很多行长——我发现这读起来更糟。所以我想知道包含该类的命名空间中的using 指令是否可以? (即使在标题中。)
  • 如果放在namespace { ... }的范围内呢?
  • 所以你完全可以写:{ using namespace blabla;类废话{}; } 并且 using 将仅适用于类?
【解决方案4】:

范围是 using 声明所在的任何范围。

如果这是全局范围,那么它将在全局范围内。如果它在头文件的全局范围内,那么它将在包含该头文件的每个源文件的全局范围内。

因此,一般建议是避免在头文件的全局范围内使用声明

【讨论】:

  • 这还不够强大。将避免替换为不要
  • 但避免比不避免强。 “避免撞到其他汽车”
【解决方案5】:

在引用的情况下,文件(“翻译单元”),这意味着是的,包含它的每个文件。

您也可以将 using 语句放在类中,在这种情况下,它仅对该类有效。

通常,如果您需要在标头中指定命名空间,最好只对每个必要的标识符进行完全限定。

【讨论】:

  • 请注意,类中的 using 声明与类外的行为方式不同 - 例如,您不能使用它将 cout 而不是 std::cout 带入类。
【解决方案6】:

没错。范围是使用using 声明的模块。如果模块包含的任何头文件具有 using 声明,则这些声明的范围将是该模块以及包含相同头文件的任何其他模块。

【讨论】:

    【解决方案7】:

    当他们说“不要”时,有一些 cmets 是相当不合格的。太严厉了,但你要明白什么时候可以。

    using std::string 永远不行。在您自己的标头中写入using ImplementationDetail::Foo,当该标头声明ImplementationDetail::Foo 时就可以了,如果 using 声明发生在您的命名空间中。例如

    namespace MyNS {
        namespace ImplementationDetail {
            int Foo;
        }
        using ImplementationDetail::Foo;
    }
    

    【讨论】:

    • 然后header的用户可以写MyNS::Foo
    • 一个更好的例子是using boost::posix_time::ptime。当然用户可以写MyNS::ptime,但这不是世界末日,并且可能会被MyFunction(ptime a, ptime b)等功能的便利性所抵消。
    • 为什么 using std::string 永远不行?甚至在自己的命名空间中保存了很多std::前缀?
    • @thomthom 包装在您自己的命名空间等范围内是可以的。
    【解决方案8】:

    已编辑:

    作为附加信息,当语句放在“#include”之前时,将“使用”放在源文件中会影响您的标题。 (使用范围不会“向后”延伸

    header.h

    //header.h
    #include <string>
    
    std::string t1; // ok
    string t2; // ok
    

    header.cpp

    //header.cpp
    using namespace std ;
    
    #include "header.h"
    
    ...
    

    【讨论】:

    • 只有在using 语句放在#include 之前时才会出现这种情况:using 的范围不会“向后”扩展。
    • @ChristophLipka 那么这是微软的错误吗?我用 vscode 和 visual studio 测试了一下。
    • 我无法在 VS 2015 中重现您的结果。如果我输入 using namespace std; before #include "header.h"。我看到 VS 将 string t2 识别为 std::string。但是,如果我把using namespace std; 放在 #include "header.h" 之后。我看到 VS 在string t2 上报错。
    • 再次测试,我认为这是智能感知的一个错误,因为它无法编译。
    • 附加说明:通常期望头文件“按原样”工作,而不需要包含文件中的任何其他语句。因此,通常不鼓励以需要在包含文件中使用using 语句的方式编写头文件。
    猜你喜欢
    • 2019-08-01
    • 1970-01-01
    • 2016-05-10
    • 2020-04-10
    • 1970-01-01
    • 2020-06-08
    • 1970-01-01
    • 2010-10-15
    相关资源
    最近更新 更多