成员函数声明签名中的类成员可见性 [英] Class member visibility in member function declaration signature

查看:123
本文介绍了成员函数声明签名中的类成员可见性的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

为什么这样工作:

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

但这不是( a code> f 交换的地方):

But this does not (a and f swapped places):

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

表示 a 范围(在decltype内部),但添加显式 this-> 使其工作。

saying that a is not declared in that scope (inside decltype) but adding explicit this-> makes it work.

推荐答案

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:


  • 正如我们在模板中一样,查找是否 / code>是否依赖。由于在 f 之前声明 a ,找到 a 指类成员。

  • 根据C ++中的模板规则,发现 a 是指当前实例化的成员,因为它是周围模板的实例化成员。在C ++中,这个概念主要用于决定名称是否依赖:如果知道一个名称指向周围模板的成员,在实例化时不一定需要查找它,因为编译器已经知道模板的代码(它被用作从其实例化的类类型的基础!)。考虑:

  • As we are in a template, a lookup is done to see whether a is dependent or not. Since a was declared prior to f, a is found to refer to a class member.
  • 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. 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!). Consider:

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


在C ++ 03中,声明 y 将是错误,因为 A< T> :: type 是一个依赖名称, c $ c> typename 在它前面。只有第一行被接受。在C ++ 11中,这种不一致性是固定的,两个类型名称都是非依赖的,不需要 typename 。如果你改变typedef为 typedef T type; 然后两个声明 x y 将使用依赖类型,但两者都不需要 typename ,因为您仍然命名当前实例化的成员,编译器知道你命名一个类型。

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. 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. If you change the typedef to 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.


  • 因此 a 是当前实例化的成员。但它是依赖的,因为用于声明它的类型( A )是依赖的。然而这在你的代码中没有关系。无论是否依赖,找到 a ,代码有效。

  • So a is a member of the current instantiation. But it is dependent, because the type used to declare it (A) is dependent. However this doesn't matter in your code. Whether dependent or not, a is found and the code valid.
template <typename A>
struct S {
    template <typename B>
    auto f(B b) ->
        decltype(a.f(b))
    {
    }
    A a;
};

在此代码中,再次查看 a 以查看其是否是依赖的和/或其是否是当前实例化的成员。但是,由于我们上面已经知道,在尾随返回类型之后声明的成员是不可见的,所以我们没有找到 a 的声明。在C ++中,除了当前实例化的成员的概念之外,还有另一个概念:

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. But since we learned above that members declared after the trailing return type are not visible, we fail to find a declaration for a. In C++, besides the notion "member of the current instantiation", there is another notion:


  • 未知专业化。这个概念用于指代名称可能改为引用依赖于模板参数的类的成员的情况。如果我们访问了 B :: a ,那么 a 将是未知专业化的成员, em>未知在实例化时替换 B 时将显示哪些声明。

  • 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.

既不是当前成员,也不是未知专业化成员。 您的大小写适合这里,因为已知 a 永远不会是实例化时的任何实例化的成员(记住名称查找找不到 a ,因为它在 f 后声明。

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 未找到任何声明的查找是 binding ,这意味着在实例化时没有可以找到声明的其他查找。非依赖名称在模板定义时间查找。现在GCC正确地给你一个错误(但注意,一如既往,一个不良形式的模板不需要立即诊断)。

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. 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).

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

在这种情况下,您添加了 this GCC接受。再次是 this-> 之后的 a 名称是查看其是否可能是当前实例化。但是再次因为成员在尾部返回类型中的可见性,没有找到声明。因此,该名称被认为不是当前实例化的成员。因为没有办法在实例化时, S 可以有额外的成员 a 可以匹配(没有基类 S 取决于模板参数),该名称也不是未知专门化的成员。

In this case, you added this and GCC accepted. The name a that follows this-> again is lookup at to see whether it might be a member of the current instantiation. 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.

再次C ++没有规则使 this-> a 从属。但是它使用 this-> ,因此必须指向 S 当它被实例化时!所以C ++标准说

Again C++ has no rules to make this->a dependent. However it uses this->, so the name must refer to some member of S when it is instantiated! So the C++ Standard says


类似的,如果一个类成员访问表达式中的id表达式的对象表达式的类型是当前实例化不引用当前实例化的成员或未知专门化的成员,即使不包含成员访问表达式的模板未被实例化,程序也不成立;无需诊断。

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; no diagnostic required.

此代码不需要诊断(而GCC实际上不提供诊断)。成员访问表达式 this-> a 中的id-expression a 在C ++ 03中是依赖的,因为该标准中的规则没有像C ++ 11中那样精细和微调。暂时让我们想象C ++ 03有 decltype 和尾随的返回类型。

Again no diagnostic is required for this code (and GCC actually doesn't give it). 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. For a moment let's imagine C++03 had decltype and trailing return types. What would this mean?


  • 查找会延迟到实例化,因为 this-> a 将是依赖

  • 例如, S< SomeClass> 的查找将失败,因为 this-> a 在实例化时不会被找到(正如我们所说,尾随返回类型没有看到成员声明)。

  • The lookup would have been delayed until instantiation, because this->a would be dependent
  • 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).

因此,C ++ 11早期拒绝该代码是非常有用的。

Hence, the early rejection of that code by C++11 is good and useful.

这篇关于成员函数声明签名中的类成员可见性的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆