【问题标题】:Should a name found in a dependent base class hide a namespace name at the point of instantiation?在依赖基类中找到的名称是否应该在实例化时隐藏命名空间名称?
【发布时间】:2013-08-14 22:35:15
【问题描述】:

使用以下代码,clang 3.0 给出error: lookup of 'N' in member access expression is ambiguous,而clang 3.4 和gcc 4.8 都接受该代码而不会出错。

struct B
{
    struct M
    {
        void f()
        {
        }
    };
};

namespace N
{
    struct M
    {
        void f()
        {
        }
    };
}

template<typename>
struct A : N::M, B::M
{
    typedef B N;
};

struct D : A<int>
{
    A<int> m;
    void f()
    {
        m.N::M::f(); // found class-name 'A<int>::N' (unambiguous)
    }
};

template<typename T>
struct C : A<T>
{
    A<T> m;
    void f()
    {
        m.N::M::f(); // found namespace-name 'N' (ambiguous?)
    }
};

template struct C<int>;

在查阅标准后,我不清楚关于C&lt;T&gt;::f() 中的表达式,哪种行为是正确的。

因为N在对象表达式n(依赖)的类范围和整个后缀表达式的上下文(即函数C&lt;T&gt;::f()的范围)中都被查找,有必要将查找延迟到实例化点。

在实例化时,如果同时找到命名空间 N 和 typedef A&lt;T&gt;::N,查找将是不明确的。 N 的声明只有在没有被 A&lt;T&gt;::N 的声明隐藏时才可见。

问题是命名空间N是否应该被认为在查找N“在整个后缀表达式的上下文中”和“在定义点时被typedef A&lt;T&gt;::N隐藏模板”。

引自 C++ 工作草案标准 N3242=11-0012(2011 年 2 月):

3.4.5 类成员访问 [basic.lookup.classref]

如果类成员访问中的 id-expression 是形式的限定 id

class-name-or-namespace-name::...

.-&gt; 运算符后面的类名或命名空间名在 的上下文中查找 整个后缀表达式,并且在对象表达式的类的范围内。如果名称仅在 对象表达式的类的范围,名称应指类名。如果找到名字 仅在整个后缀表达式的上下文中,名称应引用类名或命名空间名。 如果在两个上下文中都找到该名称,则类名或命名空间名应指同一个 实体。

14.6.4 从属名称解析 [temp.dep.res]

在解析从属名称时,会考虑来自以下来源的名称:

在模板定义处可见的声明。

——来自与函数参数类型相关的命名空间的声明 实例化上下文 (14.6.4.1) 和定义上下文。

【问题讨论】:

  • 怎么会想出这样的代码?
  • @jrok:空闲时间太多
  • 在编写 C++ 解析器时总是出现这种东西;)
  • 顺便说一句,引用的标准是什么?
  • @jrok 它是 C++98 或 C++03。这在 C++11 中有所改变。 (这说明旧版本做了不同的事情。)

标签: c++ templates language-lawyer


【解决方案1】:

这在 C++11 中有所改变。你引用的文字 来自 C++03;在 C++11 之前,这是模棱两可的,因为两者 使用了查找,如果他们发现不同,那就是错误 名字。在 C++11 中,文本为:

如果类成员访问中的 id-expression 是qualified-id 形式为 class-name-or-namespace-name::... . 后面的类名或命名空间名。或 -> 运算符是 首先在对象表达式的类中查找 如果找到,则使用名称。否则会在 整个后缀表达式的上下文。 [注:见 3.4.3, 它描述了在 :: 之前的名称查找,它只会 查找类型或命名空间名称。 ——尾注]

基本上,这对类范围内的查找具有特权,并且 如果找到名称,则不会执行另一项。

至于为什么这只影响旧版本中的模板 标准:我认为(很难确定任何事情 这里)这是因为在非模板的情况下, 在整个后缀表达式的上下文中查找 在基类中找到typedef,因此两个查找都解决了 到同一个实体。在模板的情况下,查找 整个后缀表达式的上下文不考虑 记下依赖的基类,并且只找到命名空间 N。然而,在 C 实例化之后,在 类的范围找到typedef。由于两次查找 查找不同的实体,名称绑定不明确。

【讨论】:

  • 看来我需要升级我的标准副本!
  • 该更改发生在 N3242->N3291 中,就在最终的 C++11 标准之前。令人高兴的是,该更改简化了类成员访问表达式中名称查找的许多极其复杂的规则。
  • @willj 我没有亲自做过比较,但我听说N3337,C++11后的第一个草案,在内容上实际上比N3242更接近发布的标准.
猜你喜欢
  • 2013-08-15
  • 1970-01-01
  • 1970-01-01
  • 2015-01-26
  • 1970-01-01
  • 1970-01-01
  • 2019-08-28
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多