在非静态成员函数中使用时,非静态成员的名称是否依赖?

wil*_*llj 15 c++ templates language-lawyer dependent-type c++11

gcc 5.0和clang 3.6都需要typename以下示例中的关键字:

template<int n>
struct I
{
    typedef int Type;
};

template<typename T>
struct A
{
    int m;

    void f()
    {
        typedef typename I<sizeof m>::Type Type; // typename required
    }
};
Run Code Online (Sandbox Code Playgroud)

这由C++ 11标准中的以下措辞涵盖:

[temp.dep.type]/8

如果是,则类型依赖于

  • 一个simple-template-id,其中模板名称是模板参数或任何模板参数是依赖类型或依赖于类型或依赖于值的表达式

所以I<sizeof m>依赖于依赖于sizeof m价值.

[temp.dep.expr/4

以下表单的表达式从不依赖于类型(因为表达式的类型不能依赖):

sizeof unary-expression
Run Code Online (Sandbox Code Playgroud)

[temp.dep.constexpr]/2

如果unary-expression或expression是typedependent或type-id是依赖的,则以下形式的表达式是值依赖的:

sizeof unary-expression
Run Code Online (Sandbox Code Playgroud)

所以sizeof m只依赖m于依赖.

[expr.prim.general]/8

在非静态成员函数的定义中,将指定非静态成员的标识符转换为类成员访问表达式

所以m是一个类的成员访问表达构件.

[temp.dep.type/4

名称是当前实例化成员(如果是)

  • 一个id-expression表示类成员访问表达式(5.2.5)中的成员,对象表达式的类型是当前实例化,而id-expression在查找时(3.4.5),至少指的是当前实例化的一个成员或其非依赖基类.

所以它似乎m是当前实例化的成员.

[temp.dep.type]/5

如果是,则名称是未知专业化的成员

  • 一个id-expression,表示类成员访问表达式(5.2.5)中的成员

    • 对象表达式的类型是当前实例化,当前实例化具有至少一个从属基类,并且id-expression的名称查找未找到当前实例化的成员或其非依赖基类; 要么

    • 对象表达式的类型是依赖的,而不是当前的实例化.

所以m不是未知专业化的成员 - 通过名称查找可以找到它是当前实例化的成员.

[temp.dep.expr]/3

如果id-expression包含,则它依赖于类型

  • 通过名称查找与一个或多个使用依赖类型声明的声明关联的标识符,
  • 用于命名未知专业化成员的嵌套名称说明符或qualified-id

由于m属于类型int并且不是未知专业化的成员,因此这些子弹都不会使id-expression m依赖.

[temp.dep.expr]/5

如果表达式引用当前实例化的成员并且引用的成员的类型是相关的,或者类成员访问表达式引用未知特化的成员,则类成员访问表达式(5.2.5)是类型相关的.

m转换为类成员访问表达式时,它仍然不依赖,因为它不引用未知专门化的成员.

应该m被视为依赖?在相关的说明中,应该this->m被视为依赖?怎么样std::declval<A>().m

编辑

最后,应该&A::m依赖?

wil*_*llj 1

答案取决于是否m可以查找以确定它是当前实例化的成员。id-表达式m转换为类成员访问(*this).m,这意味着类成员访问中的限定名查找规则适用。

\n\n

通常,无法确定类型相关表达式的类型。目前还不完全清楚是否应该对(*this).和进行例外处理this->。包含的表达式this与类型相关,但两者(*this).this->明确命名当前实例化。

\n\n

m

\n\n

该表达式m确实不依赖于类型,因为它引用当前实例化的成员。

\n\n

在非静态成员的上下文中,m被转换为类成员访问表达式(*this).m

\n\n
\n

[class.mfct.非静态]/3

\n\n

当 id 表达式 (5.1) 不是类成员访问语法 (5.2.5) 的一部分且不用于形成指向成员 (5.3.1) 的指针时,在类成员中使用X可以被使用(5.1.1),如果名称查找(3.4)将id表达式中的名称解析为某个类的非静态非类型成员C,并且如果id表达式可能被评估或者CX或基数的类X,id-表达式被转换为类成员访问表达式(5.2.5)使用(*this)

\n
\n\n

发生转换是因为是在 类 的非静态成员中使用的m类成员。AA

\n\n
\n

[expr.prim.general]/3

\n\n

如果声明声明了类的成员函数或成员函数模板,X, the expression则此`\n 是类型为 \xe2\x80\x9c 的纯右值,指向可选的 cv-qualifier-seq 之间的 cv-qualifier-seq X\xe2\x80\x9d以及函数定义、成员声明符或声明符的结尾。它不得出现在可选的 cv-qualifier-seq 之前,并且不得出现在静态成员函数的声明中(尽管其类型和值类别是在静态成员函数中定义的,因为它们在非静态成员函数中)静态成员函数)。

\n\n

[expr.prim.general]/5

\n\n

该表达this不得出现在任何其他上下文中。[ 例子:

\n
\n\n
class Outer {\n    int a[sizeof(*this)]; // error: not inside a member function\n    unsigned int sz = sizeof(*this); // OK: in brace-or-equal-initializer\n\n    void f() {\n        int b[sizeof(*this)]; // OK\n\n        struct Inner {\n            int c[sizeof(*this)]; // error: not inside a member function of Inner\n        };\n    }\n};\n
Run Code Online (Sandbox Code Playgroud)\n\n
\n

\xe2\x80\x94结束示例]

\n
\n\n

上面的示例明确允许在非静态成员内的表达式this内使用。sizeof

\n\n
\n

[温度.dep.表达式]/2

\n\n

this如果封闭成员函数的类类型是相关的,则该类型是类型相关的

\n
\n\n

因此,this在类模板的成员函数的定义中是类型相关的。

\n\n
\n

[温度.dep.表达式]/1

\n\n

除下述情况外,如果任何子表达式是类型相关的,则表达式是类型相关的。

\n
\n\n

然而,上述内容被问题中引用的 [temp.dep.expr]/5 中的异常所推翻。

\n\n

this->m

\n\n

该表达式this->m也不依赖于类型,因为它也是一个类成员访问表达式,引用当前实例化的成员。

\n\n

std::declval<A>().m

\n\n

表达式std::declval<A>().m必须与类型相关,因为 的返回类型std::declval<A>()可能取决于 的类型A

\n\n
\n

[本地温度]/1

\n\n

与普通(非模板)类一样,类模板有一个注入类名(第 9 条)。注入的class-\n名称可以用作模板名称或类型名称。当它与模板参数列表一起使用时,\n 作为模板模板参数的模板参数,或作为友元类模板声明的详细类型说明符\n 中的最终标识符,它引用该类模板本身。否则,它相当于模板名称,后跟包含在中的类模板的模板参数<>

\n
\n\n

因此A变换为A<T>.

\n\n
\n

[温度相关类型]/8

\n\n

一个类型是依赖的,如果它是

\n\n
    \n
  • 模板参数,

  • \n
  • simple-template-id,其中模板名称是模板参数,或者任何模板参数是依赖类型或依赖于类型或值的表达式

  • \n
\n
\n\n

这证实了它A<T>是一个依赖类型,意味着它A也是依赖的。

\n\n
\n

[温度.dep.表达式]/3

\n\n

如果 id 表达式包含,则它是类型相关的

\n\n
    \n
  • 依赖的模板 ID,
  • \n
\n
\n\n

std::declval<A>类型相关的表达式也是如此。因此,std::declval<A>().m是 类型相关的,因为它包含类型相关的子表达式std::declval<A>

\n\n

&A::m

\n\n

该表达式&A::m在逻辑上必须是类型相关的,因为它的类型int A<T>::*是依赖类型。

\n\n

表达式&A::m被转换为&A<T>::m因为A是注入的类名 - 如上所示。

\n\n

A<T>::m根据 [temp.dep.expr]/3,id 表达式是类型相关的,因为它包含一个A<T>相关的 template-id。&A<T>::m因此,根据 [temp.dep.expr]/1,表达式是类型相关的。

\n\n

A::m

\n\n

表达式A::m被转换为A<T>::m因为A是注入的类名 - 如上所示。该表达式A<T>::m进一步转换为(*this).A<T>::mbecauseA<T>::m命名 的非静态成员A

\n\n

A<T>::m根据 [temp.dep.expr]/3,id 表达式是类型相关的,因为它包含一个A<T>相关的 template-id。类成员访问表达式(*this).A<T>::m引用当前实例化的成员,因此 [temp.dep.expr]/5 适用,但不会使表达式依赖于类型 - 也不与 [temp.dep.expr]/3 相矛盾。

\n\n

缺点?

\n\n

根据上面的解释,AAor命名成员的 id 表达式A<T>将变得依赖于类型,这似乎不必要且不一致。A请考虑用或限定的类型名称A<T>在同一上下文中不会依赖。

\n\n

如果 [temp.dep.expr]/5 的意图是(*this).m不依赖于类型,那么它(*this).A<T>::m也不应该依赖于类型。另一方面,其意图可能是非静态成员的名称始终是相关的。我已经在 std-discussion 上发布了一个问题来澄清这一点: https: //groups.google.com/a/isocpp.org/forum/# !topic/std-discussion/gEvZ7mmXEC8

\n

  • 但类成员访问表达式的类型依赖性在 [temp.dep.expr]/5 中描述,它优先于 [temp.dep.expr]/1 中的一般规则。 (2认同)