C++ 移位运算符、逐位运算符和sizeof运算符产生的结果的值类别是什么?

C++ 移位运算符、逐位运算符和sizeof运算符产生的结果的值类别是什么?,c++,operators,expression,language-lawyer,C++,Operators,Expression,Language Lawyer,值班操作员: 位运算符:~,&,^,| sizeof运算符:sizeof() < C++ >标准(N37 97),我只能确认 ~< /C> > PR值(5.3.1/2),而不是上面的其他值。表示以下关于函数>: prvaule(“纯”右值)是标识临时对象(或其子对象)或与任何对象无关的值的表达式。 以下表达式是PRV值: 文字(字符串文字除外),例如42或true或nullptr 函数调用/运算符表达式,如果函数或运算符的返回类型不是引用,例如str.substr(1,2)或2+2 这适用于函数

值班操作员:
位运算符:
~
&
^
|
sizeof运算符:
sizeof()

< C++ >标准(N37 97),我只能确认<代码> ~< /C> > PR值(5.3.1/2),而不是上面的其他值。

表示以下关于<强>函数>:
prvaule(“纯”右值)是标识临时对象(或其子对象)或与任何对象无关的值的表达式。
以下表达式是PRV值:

  • 文字(字符串文字除外),例如42或true或nullptr
  • 函数调用/运算符表达式,如果函数或运算符的返回类型不是引用,例如str.substr(1,2)或2+2
  • 这适用于函数,我不确定关于内置运算符的具体规则是什么。

    有一些小东西: :

    sizeof():根据标准5.3.3 pt6-标准5.19/3,size\t规定“整型常量表达式是整型或无范围枚举类型的表达式,隐式转换为prvalue,其中转换后的表达式是核心常量表达式。”从5.3.3/6和18.2/6可以推断出它是prvalue

    E1>E2:标准5.8.1“操作数应为整数或无范围枚举类型,并执行整数提升。结果类型为提升后的左操作数类型。”。根据标准4.5和相应的第7部分,积分提升意味着PR值


    &|和^:标准规定“执行通常的算术转换;(…)运算符仅适用于整数或无范围枚举操作数”

    据我所知,结果是pr值,但这只是一种推测。这似乎与和中介绍的操作数的值类别的指定方式类似

    我们可以从
    3.10节中看到,左值和右值有以下注释:

    第5条中对每个内置运算符的讨论表明 它产生的价值类别和 它需要的操作数

    但是正如我们所看到的,
    5节仅在少数情况下明确说明了值类别。在这个问题中,仅明确说明了
    &
    ~
    结果的值类别


    即使没有明确的定义,我们也能找到一些线索,为我们指明一致的方向。我们可以证明,
    >
    decltype(sizeof(char))[expr.sizeof]/6”的操作数,
    sizeof
    sizeof…
    的结果是一个类型为
    std::size\u t
    的常量,但我不确定常量是否严格地意味着prvalue。@dyp我想说,如果没有指定它,它就是一个prvalue,类似于@user2451677,我指的是rvalue、lvalue、xvalue,而不是变量类型(例如int、double)@dyp Jens的讨论有一个类似的建议,即算术转换应该意味着l到r的转换。赋值也不“返回引用”,而是产生一个左值。这些内置运算符都不是函数。@eladm26,递增运算符++(前缀)确实返回左值,这似乎与that@dyp我明白了,我的错误,我混合了函数和运算符。当您,@rabbit hole digger编辑问题的标题时,您更改了问题。非常有趣,不是吗?积分促销不适用于
    int
    s。因此,严格地说,[conv.prom]不要求对
    int
    s使用l-to-r,但我认为说提升旨在为所有操作数生成PR值并不太牵强。事实上,正如aschepler在我自己的回答中指出的,对操作数执行整数提升这一事实不足以保证operator@dyp我更新了我的答案,如果你认为这听起来合理或者我犯了错误,请告诉我。Howard Hinnant对Luc有不同的方法,这是一个有趣的阅读。有趣的是,对于
    x+=1
    x
    被转换为重写形式的prvalue
    x=x+1
    ,但运算符仍会生成一个左值。类似地,
    x++
    。我认为基本思想是算术运算符和位运算符产生的值与两个操作数不同。因此,将任一操作数作为左值返回是没有意义的。通过使用赋值或显式构造(命名实体;也包括:函数参数)创建具有该“新”值的对象。操作数
    E1
    被提升,而不是我们正在查看的表达式
    E1>E2
    。表达式可以具有提升操作数的类型,而不是prvalue;类型和值类别基本上是正交的。@aschepler是否存在生成左值的标准转换?(身份转换除外)@dyp不在第4节中,尽管13.3.3.1.4将某些引用绑定视为“转换”。为什么?@dyp您是对的,但aschepler的意思是,运算符的实现方式可能会产生一个xvalue,例如使用一个引用临时对象的表达式。虽然我倾向于将xvalue理解为需要一个带有显式引用的表达式(3.10和8.3.2),并且我希望CPU移位指令是没有中间引用的PRValue,但这里显然存在不确定性。
    std::cout << typeid(decltype(sizeof(char))).name() << std::endl;
    std::cout << typeid(decltype(1 << 1)).name() << std::endl;
    std::cout << typeid(decltype(1 >> 1)).name() << std::endl;
    std::cout << typeid(decltype(~1)).name() << std::endl;
    std::cout << typeid(decltype(1 & 1)).name() << std::endl;
    std::cout << typeid(decltype(1 | 1)).name() << std::endl;
    std::cout << typeid(decltype(1 ^ 1)).name() << std::endl;
    
    std::cout << "-------------" << std::endl;
    std::cout << typeid(decltype(1U << 1)).name() << std::endl;
    std::cout << typeid(decltype(1U >> 1)).name() << std::endl;
    std::cout << typeid(decltype(~1U)).name() << std::endl;
    std::cout << typeid(decltype(1U & 1)).name() << std::endl;
    std::cout << typeid(decltype(1U | 1)).name() << std::endl;
    std::cout << typeid(decltype(1U ^ 1)).name() << std::endl;
    
    std::cout << "-------------" << std::endl;
    std::cout << typeid(decltype(1L << 1)).name() << std::endl;
    std::cout << typeid(decltype(1L >> 1)).name() << std::endl;
    std::cout << typeid(decltype(~1L)).name() << std::endl;
    std::cout << typeid(decltype(1L & 1)).name() << std::endl;
    std::cout << typeid(decltype(1L | 1)).name() << std::endl;
    std::cout << typeid(decltype(1L ^ 1)).name() << std::endl;
    
    unsigned int
    int
    int
    int
    int
    int
    int
    -------------
    unsigned int
    unsigned int
    unsigned int
    unsigned int
    unsigned int
    unsigned int
    -------------
    long
    long
    long
    long
    long
    long
    
    template<typename T>
    struct value_category {
        // Or can be an integral or enum value
        static constexpr auto value = "prvalue";
    };
    
    template<typename T>
    struct value_category<T&> {
        static constexpr auto value = "lvalue";
    };
    
    template<typename T>
    struct value_category<T&&> {
        static constexpr auto value = "xvalue";
    };
    
    // Double parens for ensuring we inspect an expression,
    // not an entity
    #define VALUE_CATEGORY(expr) value_category<decltype((expr))>::value
    
    int x = 10, y = 2 ;
    int &xr = x ;
    int &yr = y ;
    
    std::cout << VALUE_CATEGORY( x << y ) << std::endl ;
    std::cout << VALUE_CATEGORY( 10 << 2 ) << std::endl ;
    std::cout << VALUE_CATEGORY( xr << yr ) << std::endl ;
    
    std::cout << VALUE_CATEGORY( x | y ) << std::endl ;
    std::cout << VALUE_CATEGORY( 10 | 2 ) << std::endl ;
    
    std::cout << VALUE_CATEGORY( x ^ y ) << std::endl ;
    std::cout << VALUE_CATEGORY( 10 ^ 2 ) << std::endl ;
    
    std::cout << VALUE_CATEGORY( sizeof( int ) ) << std::endl ;
    std::cout << VALUE_CATEGORY( sizeof( x ) ) << std::endl ;