[HN Gopher] C++: A prvalue is not a temporary
___________________________________________________________________
C++: A prvalue is not a temporary
Author : ingve
Score : 28 points
Date : 2025-10-31 10:52 UTC (6 days ago)
(HTM) web link (blog.knatten.org)
(TXT) w3m dump (blog.knatten.org)
| dzdt wrote:
| [Edited] For anyone like me stuck on his language: the phrase
| "move from" should be understood as a technical term loosely
| related to the English language meaning of the words. I think the
| post would be better if he explained this terminology; as it is
| you have to know an awful lot about the topic he is writing about
| to even parse what he is saying.
|
| There is a pretty good stack overflow post that quuxplusone
| linked below. How they explain it: Moving from
| lvalues Sometimes, we want to move from lvalues. That
| is, sometimes we want the compiler to treat an lvalue as if it
| were an rvalue, so it can invoke the move constructor, even
| though it could be potentially unsafe. For this purpose, C++11
| offers a standard library function template called std::move
| inside the header <utility>. This name is a bit unfortunate,
| because std::move simply casts an lvalue to an rvalue; it does
| not move anything by itself. It merely enables moving. Maybe it
| should have been named std::cast_to_rvalue or std::enable_move,
| but we are stuck with the name by now.
| quuxplusone wrote:
| "Move" in the sense of
| https://stackoverflow.com/questions/3106110/what-is-move-sem...
|
| Now, if you don't know what "move semantics" is, then "lvalues
| can't be moved from" isn't terribly helpful, and if you do then
| it's tautological, so I'm not saying you're wrong to criticize.
| :) But in a C++ context, "move" does have a single specific
| meaning -- the one he's using properly if opaquely-to-
| non-C++ers.
| Conscat wrote:
| "move" means to pass into an r-value reference function
| parameter, for instance a move constructor, move assignment
| operator, or forwarding reference.
| cjensen wrote:
| He has a good article on that at [1]
|
| But here's the gist: sometimes you have an object you want to
| copy, but then abandon the original. Maybe it's to return an
| object from a function. Maybe it's to insert the object into a
| larger structure. In these cases, copying can be expensive and
| it would be nice if you could just "raid" the original object
| to steal bits of it and construct the "copy" out of the raided
| bits. C++11 enabled this with rvalue references, std::move, and
| rvalue reference constructors.
|
| This added a lot of "what the hell is this" to C++ code and a
| lot of new mental-model stuff to track for programmers. I
| understand why it was all added, but I have deep misgivings
| about the added complexity.
|
| [1] https://blog.knatten.org/2018/03/09/lvalues-rvalues-
| glvalues...
| neonz80 wrote:
| I find that this can reduce overall complexity. It makes it
| possible to use objects that can not be copied (such as a
| file descriptor wrapper) and moving can in most cases not
| fail. Without move semantics you'd have to use smart pointers
| to get similar results but with extra overhead.
| Night_Thastus wrote:
| Interesting stuff! I knew about lvalues and rvalues but I never
| knew about concepts like "glvalue" or "prvalue" or "xvalue" that
| the linked page talks about.
|
| It makes sense that C++ avoids unnecessary copying or object
| creation _whenever possible_ , that's pretty much C++'s M.O.
| im3w1l wrote:
| > People sometimes call this "a temporary", but, as is the main
| point of this article, that's not necessarily true.
|
| Old habits die hard? It used to always create a temporary right?
| gpderetta wrote:
| > It used to always create a temporary right?
|
| Temporaries could still be elided in some cases before, but the
| semantics were still understood in terms of temporary objects.
|
| Now some forms of elision are mandatory and elision+RVO
| semantics are understood as objects being directly created into
| the final named location.
| p0w3n3d wrote:
| C++ is so complicated that I had to almost fail my exam and few
| years later I had to relearn C, get some experience in a real
| business project, and then I could start learning C++.
|
| I find that understanding how memory is layed out in executable,
| how the C works in terms of stack, symbols etc is the
| introductory knowledge I had to obtain to even think about C++.
| Not sure what's there now, because I saw recently some great
| compiler warnings, but I'm pretty sure that I did convert a
| prvalue to a pointer reference (&) at least once in my life and
| later failed with memory problems, but no compiler errors
| saghm wrote:
| Getting failures later after coercing something to a reference
| is even easier than that; just deference a null pointer when
| passing to an argument that takes a reference; no warnings or
| errors! https://godbolt.org/z/xf5d9jKeh
| steveklabnik wrote:
| When people say "Rust is just as complex as C++," I think value
| categories are a great example of why it's actually simpler, even
| if it also seems complex. C++ has three primary categories:
| prvalue, xvalue, and lvalue. There's also glvalue and rvalue.
| Rust has two: a place (which is a glvalue) and a value (which is
| a prvalue).
|
| C++ needs those extra categories, they exist for good reasons.
| But it is more complex.
| Sharlin wrote:
| To be fair, though, Rust really needs something morally like
| prvalues, to solve the heap initialization problem aka
| `Box::new([42; 10_000_000])`
| steveklabnik wrote:
| Yes, it is possible that Rust will add more complexity here
| specifically, but also just in general. Just how it goes :)
| koito17 wrote:
| Additionally, the "r" and "l" may lead one to incorrectly guess
| that rvalues and lvalues are related to their position in an
| expression. But alas, they aren't; there are lvalues that
| cannot appear in the left-hand side of an expression.
| stonemetal12 wrote:
| This is like saying 1 + 2 isn't addition because the compiler
| will optimize it away. It isn't an addition instruction in the
| emitted code but logically speaking it is addition.
|
| Similarly just because a compiler may optimize a prvalue away
| doesn't change the fact that a prvalue by definition of the
| language is a temp.
| Sesse__ wrote:
| The article specifically points out that this isn't about
| optimization. A temporary will not be created even with -O0
| (you can observe this by putting logging into the copy and move
| constructors).
___________________________________________________________________
(page generated 2025-11-06 23:00 UTC)