3

I asked this question: static_assert of const Variable

And apparently it comes down to the question does a floating point lvalue get converted to an rvalue for the purposes of comparison?

So in this code does an lvalue-to-rvalue conversion occur?

const float foo = 13.0F;
static_assert(foo > 0.0F, "foo must be greater than 0.");
Jonathan Mee
  • 37,899
  • 23
  • 129
  • 288
  • `foo` is a `float`. `0.0` is a `double`. There *has to be* a conversion in order to compare the two since the original types don't match. – Jesper Juhl Oct 27 '17 at 17:41
  • @JesperJuhl Thanks, I made a copy paste error. I've corrected it. I'm asking about the case where the literal is of the same type of the lvalue. – Jonathan Mee Oct 27 '17 at 17:43
  • To whom it may concern this seems to be the sticking point of the "values and expressions" section of [this answer](https://stackoverflow.com/a/20999389/2642059) – Jonathan Mee Oct 30 '17 at 12:42

1 Answers1

2

Yes, it is performed. Basically, it's all because 3.0 > 1.2 is a well-formed expression, that contains nothing but prvalues for operands.

First, [expr]/9 states (emphasis mine) that

Whenever a glvalue expression appears as an operand of an operator that expects a prvalue for that operand, the lvalue-to-rvalue, array-to-pointer, or function-to-pointer standard conversions are applied to convert the expression to a prvalue.

So the question really boils down to "Do the relational operators expect prvalues for operands"? And the answer to that is also yes. For we need to consider [expr.rel]/1:

relational-expression:
  shift-expression
  relational-expression < shift-expression
  relational-expression > shift-expression
  relational-expression <= shift-expression
  relational-expression >= shift-expression

The operands shall have arithmetic, enumeration, or pointer type. The operators < (less than), > (greater than), <= (less than or equal to), and >= (greater than or equal to) all yield false or true. The type of the result is bool.

The above grammar production is the important bit. We can follow it (I won't do it entirely here) and reduce shift-expression to a primary-expression. And one of the productions of a primary-expression is a literal. For which it is said in [expr.prim.literal]:

A literal is a primary expression. Its type depends on its form. A string literal is an lvalue; all other literals are prvalues.

And because most literals are prvalues, I think it's safe to say the relational operators expect prvalues for operands.

StoryTeller - Unslander Monica
  • 165,132
  • 21
  • 377
  • 458
  • This is a great answer, but man if I don't struggle with glvalues and prvalues. I have to look them up every time I read them. rvalues and lvalues are familiar and safe to me since they've been around for basically the entire time I've been a C++ programmer... But... Do you have a heuristic or something that you use to help think about glvalues and prvalues? – Jonathan Mee Jan 16 '18 at 13:11
  • @JonathanMee - I think it's safe to say that a prvalue is very much like what C considers an rvalue. I.e. anything that isn't assignable. A "pure value", possibly without an object to back it up. A glvalue is just anything you can assign to (assuming it isn't const). So any object you can name, either by an actual name, or a reference (lvalue or rvalue ref) to it. Barring UB, a reference always involves the referred to object occupying storage. But like all heuristics, it fails me often enough, even though I can get by with it while working day to day. – StoryTeller - Unslander Monica Jan 16 '18 at 13:17