Non-Class Rvalues Always Have Cv-Unqualified Types

non-class rvalues always have cv-unqualified types

The committee already seems to be aware that there's a problem in this part of the standard. CWG issue 690 talks about a somewhat similar problem with exactly the same part of the standard (in the "additional note" from September, 2009). I'd guess new language will be drafted for that part of the standard soon.

Edit: I've just submitted a post on comp.std.c++, noting the problem and suggesting new wording for the relevant piece of the standard. Unfortunately, being a moderated newsgroup, nearly everybody will probably have forgotten this question by the time it makes it through the approval queue there.

Cv-qualifications of prvalues in C++14

According to the commit on github, this was done to resolve CWG1261: Explicit handling of cv-qualification with non-class prvalues

Based on comments to the question it seems there was room for surprising variations in type category of this (formally a prvalue) and that gcc formerly and MSVC currently instead used a const lvalue.

The wording tightens up the hole to be explicit that, e.g., even if this is by some compiler-internal magic a prvalue of type X* const, prior to any further analysis it is adjusted to X*.

Similarly, your given example does look like a gcc bug. Possibly decltype isn't looking at the value type before applying the c-style cast.

The reason it's now a note in [basic.lval]/4 is that it's now a consequence of the new text in [expr]/6, rather than specifying the rule in [basic.lval]/4.

Full credit to T.C. for having basically answered this in the comments on the question, including the reference to the gcc bug-fix, and various other examples of previously under-specified behaviours for cv-qualified non-class non-array prvalues.

Good practice with equalities for non-class enums (rvalues on left?)

would it be considered good practice to do keep rvalues on the left for enumerations as well?

This is not precisely "good practice" even with literals, actually. This concept appeared in the past, but for most people having a literal on the right is much more readable (and also more consistent with the math notion that variables, specially ones that change the most, come first).

Trading off typo prevention for readability isn't always desirable. Readable code is in fact an old concept that has never changed and probably will never be. So at the end, stick with what makes more sense and is more easier to maintain. Preventing these kind of errors should be left to the compiler, and any decent compiler will trigger a warning in those cases.

Are rvalues always constant?

There is a common misunderstanding around the lvalue/rvalue terms. They do not refer to variables, but rather to expressions. An expression can yield either an lvalue or an rvalue, and that can be either const or non-const.

In particular, in your code the expression i on the right hand side of the definition int j = i; is an lvalue expression, not an rvalue. For the purpose of assignment there is an lvalue to rvalue conversion and then that is assigned to the newly declared variable.

Cont-ness is an orthogonal concept --in most cases-- and relates to whether you can or cannot mutate the object that you are dealing with.

int f();
int& g();
const int& h();
const int k();

int main() {
f(); // non-const rvalue expression
g(); // non-const lvalue expression
h(); // const lvalue expression
k(); // const rvalue expression
f() = 5; // error, cannot assign to an rvalue
g() = 5; // correct, can modify a non-const lvalue
h() = 5; // error, cannot modify a constant lvalue
}

Other examples require the use of user defined types:

struct test {
void foo() { x = 5; }
void bar() const;
int x;
};
test f();
const test g();
int main() {
f().foo(); // f() is a non-const rvalue,
// but you can call a method on the resulting object
g().foo(); // g() is a const rvalue,
// you cannot call a mutating member function
g().bar(); // but you can call a const member function
}

PODs, non-PODs, rvalue and lvalues

Rvalues are what you get from expressions (a useful simplification taken from the C standard, but not worded in C++ standardese). Lvalues are "locator values". Lvalues can be used as rvalues. References are always lvalues, even if const.

The major difference of which you have to be aware can be condensed to one item: you can't take the address of an rvalue (again, not standardese but a useful generalization of the rules). Or to put it another way, you can't fix a precise location for an rvalue—if you could, then you'd have an lvalue. (You can, however, bind a const& to an rvalue to "fix it in place", and 0x is changing the rules drastically.)

User-defined types (UDTs), however, are slightly special: you can convert any rvalue into an lvalue, if the class's interface allows it:

struct Special {
Special& get_lvalue() { return *this; }
};
void f() {
// remember "Special()" is an rvalue
Special* p = &Special().get_lvalue(); // even though you can't dereference the
// pointer (because the object is destroyed), you still just took the address
// of a temporary

// note that the get_lvalue() method doesn't need to operate on a const
// object (though that would be fine too, if the return type matched)
}

Something similar is happening for your A() = a, except through the compiler-supplied assignment operator, to turn the rvalue A() into *this. To quote the standard, 12.8/10:

If the class definition does not explicitly declare a copy assignment operator, one is declared implicitly. The implicitly-declared copy assignment operator for a class X will have the form

X& X::operator=(const X&)

And then it goes on with more qualifications and specs, but that's the important bit here. Since that's a member function, it can be called on rvalues, just like Special::get_lvalue can be, as if you had written A().operator=(a) instead of A() = a.

The int() = 1 is explicitly forbidden as you discovered, because ints don't have operator= implemented in the same way. However, this slight discrepancy between types doesn't matter in practice (at least not that I've found).


POD means Plain Old Data and is a collection of requirements that specify using memcpy is equivalent to copying. Non-POD is any type for which you cannot use memcpy to copy (the natural opposite of POD, nothing hidden here), which tends to be most types you'll write in C++. Being POD or non-POD doesn't change any of the above, and is really a separate issue.

what is the difference returning const or non-const in primative types c++

A const modifier on primitive return types will be ignored.

See also this question: Should I return const objects?

What constitutes of RValues?

  1. Do constants constitute RValues? const int x = 0; is maniputable at
    least one time.

In your declaration, x is neither an rvalue or lvalue, it is called a declarator-id. (See grammar of 8/4 in C++03) When it is used in a (sub-)expression it is a non-modifiable lvalue which can be initialized to any constant expression.

2.Now, the temporary objects created by the compiler are also RValues even when they have maniputable memory regions. Why is that so?
Because they cannot be modified by "users"? Is this the reason?

Rvalue of a class type can either be modifiable or non-modifiable but rvalues of built-in types are always cv-unqualified.

§ 3.10/9 Class rvalues can have cv-qualified types; non-class rvalues
always have cv-unqualified types.

Consider this example:

struct S {
int x;
void f(int s) { x = s; }
};

// ill-formed: Cannot assign to an rvalue of built-in type.
S().x = 42;

// Valid: A member function can be used to modify the referent of rvalue.
S().f(42);

The sub-expression S() creates an rvalue of class type whose lifetime is the end of ; full-expression.

return const value for built-in type

const means that during the lifetime of an object, the value of the object does not change.

The Standard itself notes that hence, const does not make sense on prvalues of nonclass or nonarray prvalues. Such expressions never refer to objects (at least if the expression originates from user code. The language itself may create prvalues during reference binding which magically refer to temporary objects. IMHO, though, these should be xvalues instead). Hence since there is no object, there is no lifetime. And hence, there is nothing to be held "const".

How to force the compiler not to accept rvalues when passing const reference in constructors

In case of constructor, deleting is not only most elegant, but a recommended method ( rule of five - rule of zero).

b.setA( A{} ); would accept both rvalue reference and general reference.

B b(A{});
A a;
b.setA( a );

To avoid this you have to use delete overloaded version:

void setA(const A&& arg) = delete;


Related Topics



Leave a reply



Submit