Shift operators: <<
>>
bit-wise operators: ~
, &
, ^
, |
sizeof operator: sizeof()
Per the C++ standard (n3797), I can only confirm that ~
yields prvalue (5.3.1/2), but not the others above.
Shift operators: <<
>>
bit-wise operators: ~
, &
, ^
, |
sizeof operator: sizeof()
Per the C++ standard (n3797), I can only confirm that ~
yields prvalue (5.3.1/2), but not the others above.
As far as I can tell the results are prvalues but that is just a speculative. This seems to be under-specified in a similar way to the value categories of operands which is covered in What is the value category of the operands of C++ operators when unspecified? and Does the standard mandate an lvalue-to-rvalue conversion of the pointer variable when applying indirection?.
We can see from section 3.10
Lvalues and rvalues has the following note:
The discussion of each built-in operator in Clause 5 indicates the category of the value it yields and the value categories of the operands it expects.
but as we can see section 5
only spells out the value category explicitly in a few cases. In the case of this particular question only spells out explicitly the value category of the result for &
and ~
.
Even though it is underspecified we can find clues that point us in a consistent direction. We can make an argument that the operands for >>
, <<
, ^
and |
are converted to prvalues. This does not dictate the value category of the result but it does seem to exclude the result being an xvalue as per section 5
paragraph 7 which has the following note:
An expression is an xvalue if it is:
— the result of calling a function, whether implicitly or explicitly, whose return type is an rvalue reference to object type,
— a cast to an rvalue reference to object type,
— a class member access expression designating a non-static data member of non-reference type in which the object expression is an xvalue, or
— a .* pointer-to-member expression in which the first operand is an xvalue and the second operand is a pointer to data member.
In general, the effect of this rule is that named rvalue references are treated as lvalues and unnamed rvalue references to objects are treated as xvalues; rvalue references to functions are treated as lvalues whether named or not.
I don't see any reasonable argument that the result could be an lvalue so that basically leaves us with a prvalue.
So the details are as follows:
Both ~
and &
are covered by section 5.3.1
Unary operators paragraph 2 which says:
The result of each of the following unary operators is a prvalue.
The shift operators require the integral promotions which is covered in section 5.8
Shift operators paragraph 1 which says:
The operands shall be of integral or unscoped enumeration type and integral promotions are performed.
and we can see that the integral promotions require prvalues from section 4.5
Integral promotions which says in every paragraph starts with:
A prvalue of [...]
Both ^
and |
require the usual arithmetic conversions and both say:
operator applies only to integral or unscoped enumeration operands
and therefore the last clause of the usual arithmetic conversions applies which says:
Otherwise, the integral promotions (4.5) shall be performed on both operands.59
Empirical approach
Luc Danton has an empirical approach to determining the value category of an expression in his answer to Empirically determine value category of C++11 expression?. The approach uses the following code:
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
and the answer outlines the logic as follows:
an lvalue expression results in an lvalue reference type, an xvalue in an rvalue reference type, and a prvalue in just the type.
The following examples all yield prvalue (see it live):
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 ;
int
s. So, strictly speaking, [conv.prom] doesn't require l-to-r for int
s - but I think it's not too far-fetched to say that the promotions are intended to produce prvalues for all operands. –
Sum x += 1
, x
is converted to a prvalue in the rewritten form x = x + 1
but still the operator yields an lvalue. Similarly, x++
. I think the basic idea is that the arithmetic and bitwise operators yield values different from both operands. So it doesn't make sense to return either operand as an lvalue. You create an object with that "new" value by using assignment or explicit construction (of a named entity; also: function parameters). –
Sum 637
. –
Seduction sizeof() : size_t according to Standard 5.3.3 pt6 - Standard 5.19/3 states that "An integral constant expression is an expression of integral or unscoped enumeration type, implicitly converted to a prvalue, where the converted expression is a core constant expression." From 5.3.3/6 and 18.2/6 you can deduce that it's a prvalue.
E1 << E2 and E1 >> E2 : Standard 5.8.1 "The operands shall be of integral or unscoped enumeration type and integral promotions are performed. The type of the result is that of the promoted left operand.". According to standard 4.5 and in perticular pt.7, integral promotion implies prvalue.
& | and ^ : Standard specifies that "The usual arithmetic conversions are performed; (...) operator applies only to integral or unscoped enumeration operands".
E1
is promoted, not the expression E1 << E2
or E1 >> E2
we're looking at. And an expression could have the type of a promoted operand without being a prvalue; type and value category are mostly orthogonal. –
Cardiganshire cppreference says the following about functions :
prvaule ("pure" rvalue) is an expression that identifies a temporary object (or a subobject thereof) or is a value not associated with any object.
The following expressions are prvalues:
This apply for functions, I'm not sure what exactly are the rules regarding built in operators.
There is small stuff: http://rextester.com/DUEJY28518:
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;
The result is(MSVC):
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
© 2022 - 2024 — McMap. All rights reserved.
sizeof
andsizeof...
is a constant of typestd::size_t
." But I'm not sure if constant strictly implies prvalue. – Sum&
or both? – Adaptiveoperator<<()
andoperator>>()
applied to iostreams definitely returns an lvalue. – Adaptive&
. – Jequirity