【问题标题】:Scope of the c++ using directivec++ using 指令的范围
【发布时间】:2012-09-13 01:07:15
【问题描述】:

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

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

第二句和第三句究竟是什么意思?请举例。

这是我试图理解的代码:

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”。

【问题讨论】:

  • 这里的行为是未定义的。 namespace B 内部的两个 i 实例都引用同一个变量(只要看到它的 declarator,即在初始化程序之前,它就会隐藏 A::i)。所以它被初始化为自己的垃圾值增加了 11。
  • 好吧,为什么它会在using namespace A 之后隐藏A::i。这就是标准中的段落所说的吗?
  • @ThomasMcLeod:这就是阴影的意思:隐藏了定义。由于您没有限定在表达式中对 i 的使用,它从查找中获取。由于 C++ 允许您引用刚刚在初始化表达式中定义的变量,i 将是新变量,而不是来自 A 的变量。
  • 这里让我觉得很奇怪,也就是说,它允许在编译时添加非常量数据?
  • 由于与昨天完全相同的原因,它仍然未定义。

标签: c++ namespaces c++11 standards using-directives


【解决方案1】:

代码中的using 语句无关紧要。在评估 B::i 的初始化程序时,B::i 已经在作用域内。您可以通过删除using 语句来简单地证明这一点;您的代码应该以相同的方式编译和运行。在任何情况下,B::i 的值最终都是未定义的,因为它依赖于未初始化的值(即 B::i 在评估初始化程序时的值)。

【讨论】:

    【解决方案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;
    

    名称i 出现在“包含使用指令和指定命名空间的最近封闭命名空间”中; using-directive 出现在namespace B 中,而指定的命名空间是namespace A;最近的封闭命名空间是全局命名空间,所以i 显示为::i。这意味着如果名称 i 已经存在于全局命名空间中,则代码不明确。

    举个更复杂的例子:

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

    int j = i 行,i 出现在 using 指令(即 A::D)和指定命名空间(A::B::C)的最近封闭命名空间中,即 A。因此,在 using 指令之后的 A::D 内,以及在 A::D::E 内,非限定名称 i 可以引用 A::B::C::i,显示为 A::i,隐藏任何 ::i,与任何 A::i 冲突,并被任何A::D::iA::D::E::i (在A::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::i,并不意味着它实际上存在;限定名称 A::i 将继续仅引用实际名称 A::i(如果存在)。

    【讨论】:

    • 我仍然不确定这应该如何工作。那么当标准说“出现”时,这仅仅是为了隐藏名称,也就是遮蔽吗?如果是这种情况,那么上面的代码应该会导致int j = i 的名称冲突。
    • @ThomasMcLeod 你指的是哪个代码?哪个i 发生冲突?
    • 我的意思是,如果我们注释掉声明 int i = 5;int i = 6;,那么在声明 int j = i; 中应该存在涉及 i 的名称冲突。但在上面的代码中,A::D::iA::D::E::i 的存在掩盖了碰撞。
    • @ThomasMcLeod 是的,没错。正如您所注意到的,冲突仅对声明 int j = i 很重要,其中不合格的名称 i 不明确(如果 A::D::iA::D::E::i 不存在)。
    猜你喜欢
    • 2020-08-31
    • 1970-01-01
    • 2013-10-26
    • 2013-05-30
    • 1970-01-01
    • 2014-11-24
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多