简体   繁体   English

成员函数声明签名中的类成员可见性

[英]Class member visibility in member function declaration signature

Why does this work: 为什么这样做:

template <typename A>
struct S {
    A a;
    template <typename B>
    auto f(B b) ->
        decltype(a.f(b))
    {
    }
};

But this does not ( a and f swapped places): 但这不是( af交换位置):

template <typename A>
struct S {
    template <typename B>
    auto f(B b) ->
        decltype(a.f(b))
    {
    }
    A a;
};

saying that a is not declared in that scope (inside decltype) but adding explicit this-> makes it work. a未在该范围内声明(在decltype内),但添加显式this->使其工作。

template <typename A>
struct S {
    A a;
    template <typename B>
    auto f(B b) ->
        decltype(a.f(b))
    {
    }
};

This works because within a trailing return type, members of the surrounding class are visible. 这是有效的,因为在尾随返回类型中,周围类的成员是可见的。 Not all members, but only the members that are declared prior to it (in a trailing return type, the class is not considered to be complete, as opposed to function bodies). 不是所有成员,而只是在它之前声明的成员(在尾随返回类型中,该类被认为是完整的,而不是函数体)。 So what is done here: 那么这里做了什么:

  • As we are in a template, a lookup is done to see whether a is dependent or not. 正如我们在模板中一样,进行查找以查看a是否依赖。 Since a was declared prior to f , a is found to refer to a class member. 由于a被之前声明fa被发现指一类构件。
  • By the template rules in C++, it is found that a refers to a member of the current instantiation since it is a member of instantiations of the surrounding template. 通过C ++中的模板规则,发现a引用当前实例化的成员,因为它是周围模板的实例化的成员。 In C++, this notion is used mainly to decide whether names are dependent: If a name is known to refer to the surrounding template's members, it is not necessarily needed to be looked up when instantiating, because the compiler already knows the code of the template (which is used as the basis of the class type instantiated from it!). 在C ++中,这个概念主要用于决定名称是否依赖:如果已知名称引用周围模板的成员,则在实例化时不一定需要查找,因为编译器已经知道模板的代码(用作从它实例化的类类型的基础!)。 Consider: 考虑:

     template<typename T> struct A { typedef int type; void f() { type x; A<T>::type y; } }; 

In C++03, the second line declaring y would be an error, because A<T>::type was a dependent name and needed a typename in front of it. 在C ++ 03中,声明y的第二行将是一个错误,因为A<T>::type是一个依赖名称,并且在它前面需要一个typename Only the first line was accepted. 只有第一行被接受了。 In C++11, this inconsistency was fixed and both type names are non-dependent and won't need a typename . 在C ++ 11,这种不一致性是固定的,这两个类型的名称都是非相关的,并且不会需要一个typename If you change the typedef to typedef T type; 如果将typedef更改为typedef T type; then both declarations, x and y will use a dependent type, but neither will need a typename , because you still name a member of the current instantiation and the compiler knows that you name a type. 然后两个声明, xy将使用依赖类型,但都不需要typename ,因为你仍然命名当前实例化的成员,并且编译器知道你命名一个类型。

  • So a is a member of the current instantiation. 所以a是当前实例化的成员。 But it is dependent, because the type used to declare it ( A ) is dependent. 但它是依赖的,因为用于声明它的类型( A )是依赖的。 However this doesn't matter in your code. 但是,这在您的代码中无关紧要。 Whether dependent or not, a is found and the code valid. 无论相关与否, a是发现和代码有效。

template <typename A>
struct S {
    template <typename B>
    auto f(B b) ->
        decltype(a.f(b))
    {
    }
    A a;
};

In this code, again a is looked up to see whether it is dependent and/or whether it is a member of the current instantiation. 在此代码中,再次查找a以查看它是否依赖和/或它是否是当前实例化的成员。 But since we learned above that members declared after the trailing return type are not visible, we fail to find a declaration for a . 但由于我们上面了解到成员尾随返回类型后声明是不可见的,我们无法找到一个声明a In C++, besides the notion "member of the current instantiation", there is another notion: 在C ++中,除了“当前实例化的成员”之外,还有另一个概念:

  • member of an unknown specialization . 未知专业的成员 This notion is used to refer to the case where a name might instead refer to a member of a class that depends on template parameters. 此概念用于指代名称可能改为引用依赖于模板参数的类成员的情况。 If we had accessed B::a , then the a would be a member of an unknown specialization because it is unknown what declarations will be visible when B is substituted at instantiation. 如果我们访问过B::a ,那么a将是未知专门化的成员,因为在实例化时替换B时, 未知什么声明是可见的。

  • neither a member of the current, nor a member of an unknown specialization. 既不是当前的成员,也不是未知专业的成员。 This is the case for all other names. 所有其他名称都是如此。 Your case fits here , because it is known that a can never be a member of any instantiation when instantiation happens (remember that name lookup cannot find a , since it is declared after f ). 你的情况适合在这里 ,因为众所周知, a永远不能实例化时发生的任何实例化的成员(记住这个名字查找无法找到a ,因为它是后宣布f )。

Since a is not made dependent by any rule, the lookup that did not find any declaration is binding , meaning there is no other lookup at instantiation that could find a declaration. 由于a不依赖于任何规则,因此未找到任何声明的查找是绑定的 ,这意味着在实例化中没有可以找到声明的其他查找。 Non-dependent names are lookup up at template definition time. 在模板定义时查找非依赖名称。 Now GCC rightfully gives you an error (but note that as always, an ill-formed template is not required to be diagnosed immediately). 现在GCC正确地给你一个错误(但请注意,一如既往,不需要立即诊断出错误的模板)。


template <typename A>
struct S {
    template <typename B>
    auto f(B b) ->
        decltype(this->a.f(b))
    {
    }
    A a;
};

In this case, you added this and GCC accepted. 在这种情况下,您添加了this并接受了GCC。 The name a that follows this-> again is lookup at to see whether it might be a member of the current instantiation. 这个名字a遵循this->再次查找在看它是否可能是当前实例中的一员。 But again because of the member visibility in trailing return types, no declaration is found. 但是,由于尾随返回类型中的成员可见性,因此未找到任何声明。 Hence the name is deemed not to be a member of the current instantiation. 因此,该名称被视为不是当前实例化的成员。 Since there is no way that at instantiation, S could have additional members that a could match (there are no base classes of S that depend on template parameters), the name is also not a member of an unknown specialization. 由于在实例化时没有办法, S可以拥有a可以匹配的附加成员(没有依赖于模板参数的S基类),该名称也不是未知专业化的成员。

Again C++ has no rules to make this->a dependent. 同样,C ++没有规则可以使this->a依赖。 However it uses this-> , so the name must refer to some member of S when it is instantiated! 但是它使用this-> ,因此名称必须在实例化时引用S某个成员! So the C++ Standard says 所以C ++标准说

Similarly, if the id-expression in a class member access expression for which the type of the object expression is the current instantiation does not refer to a member of the current instantiation or a member of an unknown specialization, the program is ill-formed even if the template containing the member access expression is not instantiated; 类似地,如果对象表达式的类型是当前实例化的类成员访问表达式中的id-expression不引用当前实例化的成员或未知专业化的成员,则该程序甚至是不正确的如果未实例化包含成员访问表达式的模板; no diagnostic required. 无需诊断。

Again no diagnostic is required for this code (and GCC actually doesn't give it). 同样,此代码不需要诊断(GCC实际上不会给出它)。 The id-expression a in the member access expression this->a was dependent in C++03 because the rules in that Standard were not as elaborated and fine-tuned as in C++11. 成员访问表达式this->a中的id-expression a依赖于C ++ 03,因为该标准中的规则没有像C ++ 11那样详细和精细。 For a moment let's imagine C++03 had decltype and trailing return types. 让我们想象一下C ++ 03有decltype和尾随返回类型。 What would this mean? 这意味着什么?

  • The lookup would have been delayed until instantiation, because this->a would be dependent 查找将被延迟直到实例化,因为这样 - this->a将是依赖的
  • The lookup at instantiation of, say, S<SomeClass> would fail, because this->a would not be found at instantiation time (as we said, trailing return types do not see members declared later). 例如, S<SomeClass>实例化查找将失败,因为在实例化时不会找到 - this->a (正如我们所说,尾随返回类型不会看到稍后声明的成员)。

Hence, the early rejection of that code by C++11 is good and useful. 因此,C ++ 11对该代码的早期拒绝是好的和有用的。

The body of a member function is compiled as if it was defined after the class. 编译成员函数的主体,就好像它是在类之后定义的一样。 Therefore everything declared in the class is in scope at that point. 因此,在该类中声明的所有内容都在此范围内。

However, the declaration of the function is still inside the class declaration and can only see names that precede it. 但是,函数的声明仍然在类声明中,并且只能看到它之前的名称。

template <typename A>
struct S {
    template <typename B>
    auto f(B b) ->
        decltype(a.f(b)); // error - a is not visible here

    A a;
};

template <typename A>
template <typename B>
    auto S<A>::f(B b) ->
        decltype(a.f(b))
    {
        return a.f(b);   // a is visible here
    }

The Standard says (section 14.6.2.1): 标准说(第14.6.2.1节):

If, for a given set of template arguments, a specialization of a template is instantiated that refers to a member of the current instantiation with a qualified-id or class member access expression, the name in the qualified-id or class member access expression is looked up in the template instantiation context. 如果对于给定的模板参数集,实例化了模板的特化,该模板的特殊化引用了具有qualified-id或类成员访问表达式的当前实例化的成员,则qualified-id或类成员访问表达式中的名称为在模板实例化上下文中查找。

this->a is a class-member access expression, therefore this rule applies and lookup takes place at the point of instantiation, where S<A> is complete . this->a是类成员访问表达式,因此应用此规则并在实例化时进行查找,其中 S<A>完成


Finally, this doesn't solve your problem at all, because section 5.1.1 says: 最后,这根本不能解决您的问题,因为第5.1.1节说:

If a declaration declares a member function or member function template of a class X, the expression this is a prvalue of type “pointer to cv-qualifier-seq X ” between the optional cv-qualifier-seq and the end of the function-definition , member-declarator , or declarator . 如果声明声明一个类X的成员函数或成员函数模板, 表达this是类型“指向cv限定符SEQ一个prvalue X ”可选的CV-限定符-SEQ函数定义的端部之间, 成员声明者声明者 It shall not appear before the optional cv-qualifier-seq and it shall not appear within the declaration of a static member function (although its type and value category are defined within a static member function as they are within a non-static member function). 它不应出现在可选的cv-qualifier-seq之前 ,它不应出现在静态成员函数的声明中(尽管它的类型和值类别是在静态成员函数中定义的,因为它们在非静态成员函数中) 。

So you can't use this-> here, since it is before the cv-qualifier-seq part of the function declaration. 所以你不能在这里使用this-> ,因为它是在函数声明的cv-qualifier-seq部分之前。

Wait, no it isn't! 等等,不,不是! Section 8.4.1 says 第8.4.1节说

The declarator in a function-definition shall have the form 函数定义中的声明应具有该形式

D1 ( parameter-declaration-clause ) cv-qualifier-seq opt ref-qualifier opt exception-specification opt attribute-specifier-seq opt trailing-return-type opt D1 ( parameter-declaration-clause ) cv-qualifier-seq opt ref-qualifier opt exception-specification opt attribute-specifier-seq opt trailing-return-type opt

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM