【问题标题】:Why can't we use nested type through class-member-access expression?为什么我们不能通过类成员访问表达式使用嵌套类型?
【发布时间】:2014-10-25 10:14:07
【问题描述】:

我试图理解为什么我们不能通过类成员访问表达式使用嵌套类型。 例如,我们有以下类:

struct U
{
    struct A
    {
        static int v;
        int a;
    };

    struct B
    {
       int b;
    };
};

U a;

typedef a.A T; //'a' does not name a type

int main()
{
    std::cout << typeid(a.A).hash_code(); //invalid use of 'struct U::A'
    struct a.A b;                         //trying to declare a variable of type U::A
                                          //error: expected unqualified-id before '.' token
    a.A b;                                //the same as above
                                          //error: expected unqualified-id before '.' token
    a.A.v = 5;                            //error: expected unqualified-id before '.' token
}

DEMO

标准说:

部分N3797::5.2.5/2 [expr.ref]

对于第一个选项(点),第一个表达式应完整 类类型。对于第二个选项(箭头),第一个表达式应 有指向完整类类型的指针。表达式 E1->E2 是 转换为等价形式 (*(E1)).E2; 5.2.5 的剩余部分 将只处理第一个选项(点)。在任何一种情况下, id-expression 应命名该类或其基类之一的成员

N3797::9.2/1 [class.mem] 部分给出了类成员的定义:

类的成员是数据成员、成员函数 (9.3)、嵌套 类型和枚举数。

所以我看不到这种使用嵌套类型的限制。为什么不呢?

【问题讨论】:

    标签: c++ types


    【解决方案1】:

    在 [expr.ref] 中往下看

    (4.4) 如果 E2 是嵌套类型,则表达式 E1.E2 格式不正确。

    【讨论】:

    • 这是“如果 E2 被声明为具有类型“对 T 的引用””下的一个子句,我认为这不适用于这里。
    • @luk32 "如果 E2 被声明为具有“对 T 的引用”类型,则 E1.E2 是一个左值;E1.E2 的类型是 T。否则,以下之一规则适用。"
    • 哦,我搞错了。我读了 3 次,认为它必须是对 T 的引用。不过,我会在答案中加入更多上下文。我认为制定完整的规则很重要。国际海事组织就是这样。
    • @luk32 我不确定我还能提供多少上下文,表达式根本无效。
    • @Christophe 这将是一个带有无效类型说明符的声明(我猜你的意思是a.A,但这并不重要,因为两者都无效)。
    【解决方案2】:

    标准是谈论变量所代表的对象的类型。
    您需要访问类的范围,而不是变量。

    要从U 访问嵌套类的成员,您需要从该类型创建一个成员,有两种方法可以做到这一点:

    struct U
    {
        struct A
        {
            static int v;
            int a;
        } VarNameForA;
    
        struct B
        {
           int b;
        };
        B VarNameOfB;
    };
    

    使用范围运算符:: 访问类型。

    这是你尝试过的所有事情的方式:

    U a;
    
    typedef decltype(a) varType;
    typedef varType::A  nestedType;
    
    int main()
    {
        std::cout << typeid(U::A).hash_code(); 
        struct U::A b;                        
        U u;
        u.VarNameForA.a = 5;
        u.VarNameOfB.b = 6;
    
        U::A::c = 3;                          
    
    }
    

    对于使用运算符dot (.) 的成员,对于类型和静态变量,您需要使用scope :: 运算符。

    【讨论】:

    • 我认为 OP 知道 ::,但表示 N3797::5.2.5/2 另有说明。
    • 老实说,我认为这不能回答问题。问题不在于如何使代码工作并正确定义所有类型。但是为什么你不能通过. 访问类型,而给定的引文表明你应该可以。嵌套类型被列为成员,所以即使你的回答建议. 应该没问题。
    【解决方案3】:

    您尝试使用a.A 作为类型说明符。

    您提到的类成员访问规则(5.2.5)仅对表达式有效,不适用于类型说明符和类型名(5/1:“表达式是一系列运算符和指定计算的操作数。表达式可以产生一个值并可能导致副作用。")。

    您必须在标准的第 7.1.6 节中查看 类型说明符 的规则。那里没有使用运营商.。详细类型(7.1.6.3)使用:: 来组合类型名上的组件。

    clang 错误消息比您从编译器获得的更明确:“不能在类型上使用点运算符”)。

    顺便说一句,您的U 是一个空结构(没有数据),只有嵌套类型的定义。这就是为什么a.A.v = 5; 也是一个错误的原因:这里没有可以分配的左值。

    【讨论】:

    • 那么decltype(a.A) 应该工作吗?我还可以指出哪里说 5.2.5 仅适用于表达式?
    • 好问题!标准 7.1.6.2 将 decltype(expression) 定义为类型说明符。但是表达式是在值上,而不是在类型上,并且 a.A 不表示值(7.1.6.2.4:如果没有这样的实体,(...)程序是错误的
    • clang 中 decltype(a.A) 的错误信息与之前的不同。它说“不能用'.'引用'U'中的类型成员'A'”,这表明编译器在这种情况下应用了user657267提到的规则。
    • 是的,我也在 clang 上测试过,确实它给出了两个明显的错误。所以实际上有两种不同的规则禁止variable.SubType 形式。哎呀。感谢您的解释。我得到了 5.2.5.4 规则,但你解释了第二条。
    猜你喜欢
    • 2019-10-20
    • 1970-01-01
    • 2020-08-02
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多