[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)