首页 文章

是否应该在类成员访问表达式中为依赖类/名称空间名称延迟名称查找?

提问于
浏览
4

clang和gcc都拒绝以下代码

template<typename T>
void f(T t)
{
    t.Dependent::f(); // clang accepts, gcc rejects
    t.operator Dependent*(); // both reject
}

struct Dependent
{
     void f();
};

struct A : Dependent
{
     operator Dependent*();
};

template void f<A>(A);

我对该标准的解读表明这两种表达都应该被接受 .

在这两种情况下, Dependent 只能是类型名称 .

在这两种情况下,名称 Dependent 都是"looked up in the class of the object expression" t . 由于 t 是类型相关的表达式,因此应该延迟查找,直到模板被实例化为止 .

有什么我想念的吗?

EDIT :如果打算这样的名称不依赖,那么这个决定的理由是什么?我可以看到,如果它们不必延迟对 t.operator X::Dependent*t.X::Dependent::f 这样的构造的评估,它可以使实现者的生活更轻松,其中 X 可以是命名空间或类型名称 . 我不清楚这是否是当前措辞的预期或非预期的副作用 .

C工作草案N3337的相关引用:

3.4.5类成员访问[basic.lookup.classref]如果类成员访问中的id-expression是class-name-or-namespace-name :: ...形式的qualified-id,则为class-name -or-namespace-name跟随 . 或 - >运算符首先在对象表达式的类中查找,如果找到,则使用名称 . 否则,它将在整个postfix-expression的上下文中查找 . [注意:参见3.4.3,它描述了在::之前查找名称,它只能找到一个类型或命名空间名称 . -end note]如果id-expression是convert-function-id,则首先在对象表达式的类中查找其conversion-type-id,并使用名称(如果找到) . 否则,它将在整个postfix-expression的上下文中查找 . 在每个查找中,仅考虑表示其特化类型的类型或模板的名称 . 14.6.2从属名称[temp.dep]在模板内部,一些构造的语义可能因实例而异 . 这种结构取决于模板参数 . 特别是,类型和表达式可能取决于模板参数的类型和/或值(由模板参数确定),这决定了某些名称的名称查找的上下文 . 表达式可以是类型相关的(在模板参数的类型上)或依赖于值的(在非类型模板参数的值上) . [...]这些名称是未绑定的,并且在模板定义的上下文和实例化的上下文中都会查看模板实例化(14.6.4.1) . 14.6.2.1依赖类型[temp.dep.type]如果是[...],则名称是未知专门化的成员 - 表示类成员访问表达式(5.2.5)中成员的id-expression - 对象表达式的类型是当前实例化,当前实例化具有至少一个从属基类,并且id-expression的名称查找未找到当前实例化的成员或其非依赖基类;或 - 对象表达式的类型是依赖的,而不是当前的实例化 . [...]类型依赖于 - 如果是 - 未知专业化的成员,

1 回答

  • 1

    1

    以下是我认为你的第一个案例 t.Dependent::f 的工作原理 . 首先,我相信(意思是,我不完全确定)14.6.2.1p5应该说"unqualified-id"而不是"id-expression" . 但与此无关,你的名字 Dependent::f 实际上是由两个名字组成的(在标准中,每个嵌套的嵌套名称说明符后面跟着一个成员名称叫做"qualified-id",即使是语法上的,这些也不是qualified-id制作 . 所以a name foo::bar::baz 是一个限定id,但也包含另外一个"qualified-id" aswell) .

    DependentDependent::f . 前者不是"An id-expression denoting the member in a class member access expression",因此您不能简单地应用适用于 Dependent::f 的规则也适用于 Dependent .

    Dependent 因此是非依赖的,虽然需要在依赖类型中查找,但必须在定义时找到它 . 我个人认为我们应该有一个说"When looking up a qualified-id where the qualifier is type-dependent, name lookup yields an empty result."的条款,以优雅地处理这些"force name-lookup to be done immediately" . 所以无论如何,最后,我认为你的第一个案例是不正确的,因为没有找到 Dependent (第3.4条不能仅仅通过第14条的首部自行决定该名称实际上是依赖的) .

    2

    对于你的其他情况, operator Dependent ,事情变得更容易 . 你又有两个名字, Dependentoperator Dependent . 我再次找到了没有什么说 Dependent 在这里是一个从属名称(我不确定这是否是错的 . 这超出了我的意义) .

    运算符函数名称的名称查找比较(例如,名称查找哈希表的相等函数)是"they are conversion-function-ids formed with the same type"(3.8) . 这意味着为了形成名称本身(尚未进行名称查找!),您不仅需要像标识符一样提供词法拼写,而且还必须提供类型标识,这需要由 Dependent .

    t.operator Dependent* 中查找依赖的id表达式只是意味着语义类型比较被延迟 . 尝试这个,这应该工作正常

    struct Dependent; // forward decl at global scope
    t.operator Dependent*(); // in function template
    

    您的后续行动

    如果打算这样的名称不依赖,那么这个决定的理由是什么?我可以看到,如果实现者不必延迟对t.operator X :: Dependent *或tX :: Dependent :: f这样的构造的评估,它会使实现者的生活更轻松,其中X可以是命名空间或类型名称 .

    我不知道理由,但我认为你已经给了一个好点 . 这看起来非常符合在查找非限定名称时跳过依赖基类的规则 . 而且我认为适用于该案例的理由适用于此案例 . 这使得更容易在程序员的函数模板上进行推理,尤其是 .

    struct Dependent;
    
    template<typename T>
    void f(T t)
    {
        t.Dependent::f();
        t.operator Dependent*();
    }
    

    代码看起来很好,但是如果 T 恰好有 Dependent 成员,突然 Dependent 会有不同的绑定(因为首先我们被告知要查看 t 's class, and then into the surrounding scope). Under my current understanding of the templating rules, the above always refers to the surrounding scope' s Dependent ,所以上面的代码是"safe",关于这个陷阱 .

相关问题