c++ using 指令的作用域

Scope of the c++ using directive

提问人:ThomasMcLeod 提问时间:9/21/2012 最后编辑:ThomasMcLeod 更新时间:9/22/2012 访问量:1196

问:

摘自 c++11 标准的第 7.3.4.2 节:

using 指令指定指定命名空间中的名称 可以在 using 指令出现在 之后的作用域中使用 using 指令。在非限定名称查找 (3.4.1) 期间,名称 看起来好像它们是在最近的封闭命名空间中声明的 它包含 using 指令和指定的命名空间。[ 注:在此上下文中,“包含”是指“直接包含或 间接”。——尾注 ]

第二句和第三句到底是什么意思?请举例说明。

这是我试图理解的代码:

namespace A
{
    int i = 7;
}
namespace B
{
    using namespace A;
    int i = i + 11;
}
int main(int argc, char * argv[])
{
    std::cout << A::i << " " << B::i << std::endl;
    return 0;
}

它打印“7 7”而不是我所期望的“7 18”。

对不起,该程序实际上打印了“7 11”。

C++ 命名空间 C++11 标准 using 指令

评论

3赞 n. m. could be an AI 9/21/2012
此处的行为是未定义的。中的两个实例都引用同一个变量(一旦看到它的声明符,即在初始值设定项之前,它就会隐藏)。因此,它使用自己的垃圾值递增 11 进行初始化。inamespace BA::i
0赞 ThomasMcLeod 9/21/2012
好吧,为什么它后面有阴影.这是标准中的段落所说的吗?A::iusing namespace A
1赞 Nicol Bolas 9/21/2012
@ThomasMcLeod:这就是阴影的意思:定义是隐藏的。由于您没有限定您在表达式中的使用,因此它从查找中获取它。由于 C++ 允许您引用您刚刚定义的变量,即使在初始化表达式中也是如此,因此将是新变量,而不是来自 的变量。iiA
0赞 std''OrgnlDave 9/21/2012
这里有些东西让我感到非常奇怪,也就是说,它允许在编译时添加非常量数据?
1赞 n. m. could be an AI 9/21/2012
由于与昨天完全相同的原因,它仍然没有定义。

答:

5赞 Lily Ballard 9/21/2012 #1

代码中的语句无关紧要。 在计算初始值设定项时,已在范围内。您可以通过删除语句来轻松证明这一点;您的代码应该以相同的方式编译和运行。在任何情况下,的值最终都是未定义的,因为它依赖于未初始化的值(即计算初始值设定项时的值)。usingB::iB::iusingB::iB::i

4赞 ecatmur 9/21/2012 #2

消除未定义的行为:

namespace A
{
    int i = 7;
}
namespace B
{
    using namespace A;
    int tmp = i + 11;
    int i = tmp;
}
#include <iostream>
int main()
{
    std::cout << A::i << " " << B::i << std::endl;
    return 0;
}

标准的含义是在线

    int tmp = i + 11;

该名称出现在“最近的封闭命名空间中,其中包含 using 指令和指定的命名空间”;using 指令出现在 而指定的命名空间为 ;最接近的封闭命名空间是全局命名空间,因此显示为 。这意味着,如果名称已存在于全局命名空间中,则代码不明确。inamespace Bnamespace Ai::ii

对于更复杂的示例:

namespace A {
    namespace B {
        namespace C {
            int i = 4;
        }
    }
    namespace D {
        using namespace B::C;
        namespace E {
            int j = i;
        }
    }
}

在 行 中,出现在 using 指令(即 )和指定的命名空间 () 的最近封闭命名空间中,即 。因此,在 using 指令之后的 within 中,以及 在 中,非限定名称可以指显示为 , shadowing any , 与 any 冲突 , 并被 any 或 (within ) 遮蔽:int j = iiA::DA::B::CAA::DA::D::EiA::B::C::iA::i::iA::iA::D::iA::D::E::iA::D::E

int i = 1;                // shadowed by A::B::C::i appearing as A::i
namespace A {
    int i = 2;            // conflicts with A::B::C::i appearing as A::i
    namespace B {
        int i = 3;        // irrelevant
        namespace C {
            int i = 4;    // nominated; appears as A::i
        }
    }
    namespace D {
        int i = 5;        // shadows A::B::C::i appearing as A::i
        using namespace B::C;
        namespace E {
            int i = 6;    // shadows A::B::C::i appearing as A::i
            int j = i;
        }
    }
}

请注意,仅仅因为该名称在非限定名称查找期间显示,并不意味着它确实存在;限定名称将继续仅引用实际名称(如果存在)。A::iA::iA::i

评论

0赞 ThomasMcLeod 9/23/2012
我仍然不确定这应该如何工作。那么,当标准说“出现”时,这仅仅是为了隐藏名称,也就是阴影吗?如果是这种情况,那么上面的代码应该会导致 .int j = i
0赞 ecatmur 9/24/2012
@ThomasMcLeod你指的是哪个代码?哪个是碰撞?i
0赞 ThomasMcLeod 9/25/2012
我想说的是,如果我们注释掉了语句和 ,那么应该有一个涉及语句的名称冲突。但是在上面的代码中,冲突的存在和掩蔽。int i = 5;int i = 6;iint j = i;A::D::iA::D::E::i
0赞 ecatmur 9/25/2012
@ThomasMcLeod是的,没错。正如你所注意到的,冲突只对语句有影响,其中非限定名称是不明确的(如果和不存在)。int j = iiA::D::iA::D::E::i