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

时间:2014-12-31 14:18:33

标签: c++ templates c++11 language-lawyer dependent-type

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
    }
};

C ++ 11标准中的以下措辞涵盖了这一点:

  

[temp.dep.type] / 8

     

如果类型是

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

因此I<sizeof m>取决于sizeof m是否依赖于值。

  

[temp.dep.expr / 4

     

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

sizeof unary-expression
     

[temp.dep.constexpr] / 2

     

如果一元表达式或表达式是类型依赖的,则以下形式的表达式是值依赖的   或者type-id是依赖的:

sizeof unary-expression

所以sizeof m仅在m依赖时才依赖。

  

[expr.prim.general] / 8

     

内   定义非静态成员函数,将非静态成员命名的标识符转换为a   类成员访问表达式

因此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应该依赖吗?

2 个答案:

答案 0 :(得分:2)

正如您所说,sizeof m已转换为sizeof (*this).m sizeof仅在参数表达式依赖于类型时依赖,而不是,根据[temp.dep.expr] / 5:

  

如果是,类成员访问表达式(5.2.5)依赖于类型   表达式是指当前实例化的成员和   引用成员的类型是依赖的,还是类成员访问   表达是指未知专业化的成员。

m的类型不依赖,表达式也不引用未知专业化的成员 - [temp.dep.type] / 6:

  

如果名称是

,则该名称是未知专业化的成员      
      
  • id-expression 表示类成员访问表达式(5.2.5)中的成员,其中   
        
    • 对象表达式的类型是当前实例化,当前实例化至少有一个从属基类,并且    id-expression 的名称查找未找到类的成员   这是当前实例化或非依赖基类   物;或
    •   
    • 对象表达式的类型是依赖的,而不是当前的实例化。
    •   
  •   

即使(*this)的类型是依赖的,它也是当前的实例化。名称查找应该发现​​m是当前实例化的成员。

因此*this不依赖于类型,因此sizeof (*this).m不依赖。 (sizeof m也不依赖于函数定义的任何非静态数据成员初始值设定项,我在第二个删除的答案中意外地介绍了这一点。


sizeof std::declval<A>().m依赖,std::declval<A>().m必须依赖于类型 std::declval<A>().m 似乎依赖于类型,但我不确定。正如我在上面引用的[temp.dep.expr] / 5中所指定的那样,唯一的可能性是表达式中的m是未知特化的成员,我们必须证明它是。

  

如果名称是

,则该名称是未知专业化的成员      
      
  • id-expression 表示类成员访问表达式(5.2.5)中的成员,其中   
        
    • 对象表达式的类型是当前实例化,当前实例化至少有一个从属基类,并且   id-expression的名称查找找不到类的成员   这是当前实例化或非依赖基类   物;或
    •   
    • 对象表达式的类型取决于当前实例化。
    •   
  •   

以下是事实:

  • 对象表达式std::declval<A>()与类型有关。

  • std::declval<A>的查找仅在定义上下文中完成,因为它是 qualified-id ,它们永远不是依赖名称([ temp.dep] / 1)。

通过限定名称查找只找到一个declval函数模板,但是我们无法知道该候选的返回类型是否是定义时的当前实例化。特别地,add_rvalue_reference可能具有在定义时未知的特化(类似于this one的场景)。因为我们不知道std::declval<A>()是否是当前的实例化,(我们假设)它不是,这使得整个表达式依赖于类型。


&A::m的格式为& qualified-id ,[temp.dep.constexpr] / 5涵盖:

  

表单& qualified-id ,其中 qualified-id   命名当前实例化的依赖成员是   值相关。

[temp.dep.type] / 5:

  

名称是当前实例化的依赖成员(如果它是a)   当前实例化的成员,当查找时,指的是   至少有一个类是当前实例化的成员。

显然A::m是当前实例化的成员,因此&A::m与值有关。
此外,&A::m依赖于类型:根据[temp.local],子表达式A相当于A<T>,这是一个 simple-template-id 依赖模板参数。

答案 1 :(得分:1)

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

通常,无法确定类型相关表达式的类型。尚不完全清楚是否应对(*this).this->进行例外处理。包含this的表达式取决于类型,但(*this).this->都明确命名当前实例。

m

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

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

  

[class.mfct.non静电] / 3

     

当一个id-expression(5.1)不属于类成员访问语法(5.2.5)并且不用于形成指向成员(5.3.1)的指针时,在类{{1>的成员中使用在可以使用它的上下文中(5.1.1),如果名称查找(3.4)将id-expression中的名称解析为某个类X的非静态非类型成员,并且要么id-expression可能被评估,要么CC或者基类X,id-expression将被转换为类成员访问表达式(5.2.5){ {1}}

发生转换是因为X是类(*this)的非静态成员中使用的类m的成员。

  

[expr.prim.general] / 3

     

如果声明声明了一个类A this`的成员函数或成员函数模板   是可选的cv-qualifer-seq和结尾之间的类型“指向cv-qualifier-seq X的指针”的prvalue   function-definition,member-declarator或declarator。它不会出现在可选的cv-qualifier-seq之前   它不应出现在静态成员函数的声明中(尽管它的类型和值   category在静态成员函数中定义,因为它们在非静态成员函数中。)

     

[expr.prim.general] / 5

     

表达式A不得出现在任何其他上下文中。 [例如:

X, the expression
  

-end example]

上面的示例明确允许在非静态成员的this表达式中使用class Outer { int a[sizeof(*this)]; // error: not inside a member function unsigned int sz = sizeof(*this); // OK: in brace-or-equal-initializer void f() { int b[sizeof(*this)]; // OK struct Inner { int c[sizeof(*this)]; // error: not inside a member function of Inner }; } };

  

[temp.dep.expr] / 2

     如果封闭成员函数的类类型依赖

,则

this依赖于类型

因此sizeof在类模板的成员函数的定义中依赖于类型。

  

[temp.dep.expr] / 1

     

除非如下所述,否则如果任何子表达式依赖于类型,则表达式依赖于类型。

但是,问题中引用的[temp.dep.expr] / 5中的例外推翻了上述内容。

this

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

this->m

表达式this->m必须依赖于类型,因为std::declval<A>().m的返回类型可能取决于std::declval<A>().m的类型。

  

[temp.local] / 1

     

与普通(非模板)类一样,类模板具有注入类名(第9节)。注入的类 -   name可以用作模板名称或类型名称。当它与template-argument-list一起使用时,   作为模板模板参数的模板参数,或者作为详细说明类型的最终标识符   朋友类模板声明,它指的是类模板本身。否则,它是等价的   到模板名称后面跟着std::declval<A>()

中包含的类模板的模板参数

因此,A会转换为<>

  

[temp.dep.type] / 8

     

如果类型是

,则类型是依赖的      
      
  • 模板参数

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

  •   

这确认A是一种依赖类型,这意味着A<T>也是依赖类型。

  

[temp.dep.expr] / 3

     

如果id-expression包含

,则它依赖于类型      
      
  • 依赖的模板ID,
  •   

所以A<T>是一个依赖于类型的表达式。因此,A依赖于类型,因为它包含依赖于类型的子表达式std::declval<A>

std::declval<A>().m

表达式std::declval<A>逻辑上必须依赖于类型,因为它具有类型&A::m,这是一种依赖类型。

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

根据[temp.dep.expr] / 3,id-expression &A<T>::m依赖于类型,因为它包含依赖的模板ID A。因此,根据[temp.dep.expr] / 1,表达式A<T>::m依赖于类型。

A<T>

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

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

缺陷?

鉴于上述解释,命名A<T>成员{id} (*this).A<T>::mA的id-expression将变为类型依赖,这似乎是不必要和不一致的。请注意,使用AA<T>限定的类型名称不会依赖于相同的上下文。

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