https://blog.knatten.org/2025/10/31/a-prvalue-is-not-a-temporary/ Skip to content Unknown's avatar C++ on a Friday A prvalue is not a temporary Anders Schau Knatten C++ October 31, 2025October 30, 2025 3 Minutes This is part one of a series of blog posts on temporaries, copies, return value optimization, and passing by value vs. reference. A good place to start, and the point of this first article, is how a prvalue isn't necessarily a temporary. If you're entirely new to value categories (lvalues, rvalues etc.), you might want to read lvalues, rvalues, glvalues, prvalues, xvalues, help! first. lvalues vs rvalues An lvalue is an expression that can not be moved from. An rvalue is an expression that can be moved from. Let's first have a look at lvalues. Given this variable v: std::vector v{1,2,3}; If I now write the expression v somewhere, v is referring to an actual variable. I can't just move from it, as it would mess up an existing object that someone else could still be using. We call an expression like this an lvalue. For instance, if I pass my existing vector to a function useVector: useVector(v); Here, the expression v is an lvalue, and useVector can't move from it. After all, someone might want to keep using v on a following line. But if I know I won't be needing v anymore, I can turn it into an rvalue, by wrapping it in std::move: useVector(std::move(v)); Here, the expression std::move(v) is an rvalue. useVector would now be allowed to move from v. (And I must take care to not use v again, since it might have been moved into useVector.) rvalues: xvalues vs prvalues Here's another way to get an rvalue: useVector(std::vector{1,2,3}); Here, the expression std::vector{1,2,3} is also an rvalue, and again useVector would be allowed to move from it. Notice, however, that these are two different types of rvalues. std::move(v) takes an existing object and casts it to an rvalue. That type of rvalue is called an xvalue, or "eXpiring lvalue". On the other hand, std::vector{1,2,3} is a prvalue, or "pure rvalue". Unlike an xvalue, this expression never referred to an existing object in the first place. People sometimes call this "a temporary", but, as is the main point of this article, that's not necessarily true. A prvalue in itself is not a temporary. A prvalue is not an object. A prvalue just represents "the idea of the object", and only turns into a temporary when it absolutely needs to. For instance: std::vector v = std::vector{1,2,3}; Here, the prvalue std::vector{1,2,3} does not turn into a temporary that is then used to initialize v. Rather, the prvalue is used to initialize v directly, just as if you'd written std::vector v {1,2,3};. No extra temporary is created, and no copies or moves are performed. Similarly: void useVector(std::vector v); useVector(std::vector{1,2,3}); Here, useVector takes its parameter by value. std::vector{1,2,3} never turns into a temporary, the prvalue expression is instead used to initialize the parameter v directly, just like in the previous example. No extra temporary is created, and no copies of moves are performed. Temporary materialization However, if useVector takes its parameter by reference: void useVector(const std::vector& v); useVector(std::vector{1,2,3}); Now, the reference parameter v needs some object to bind to, and std::vector{1,2,3} actually turns into a temporary object that v can bind to. This is called "temporary materialization". Return values A function call that returns by value is also a prvalue ^1. So, given this definition of getVector() and a call to it: std::vector getVector(); std::vector v = getVector(); Here, the call getVector() is a prvalue which initializes v directly. There is no temporary that is then copied/moved into v. (There might be a copy involved in the return statement inside getVector(), but that's a story for the next article.) Conclusion The point is that a prvalue only materializes into a temporary as a very last resort, avoiding unnecessary copies or moves. Until it needs to materialize, it only represents "the idea of the object", i.e. what the object would be when it materializes into a temporary or is used to initialize something. It is important to note that this has nothing to do with optimization. There is no temporary std::vector to optimize away in the first place, there's just the prvalue, just the "idea of the object". And then that idea of an object can materialize into an actual object if needed. Footnotes 1. SSexpr.callP13: "A function call is an lvalue if the result type is an lvalue reference type or an rvalue reference to function type, an xvalue if the result type is an rvalue reference to object type, and a prvalue otherwise." -[?] --------------------------------------------------------------------- Discover more from C++ on a Friday Subscribe to get the latest posts sent to your email. Type your email... [ ] Subscribe Share this: * Share * * Click to share on Bluesky (Opens in new window) Bluesky * Click to share on LinkedIn (Opens in new window) LinkedIn * Click to share on Reddit (Opens in new window) Reddit * Click to share on Mastodon (Opens in new window) Mastodon * Like Loading... Related Unknown's avatar Published by Anders Schau Knatten Computer Programmer since 1995. View all posts by Anders Schau Knatten Published October 31, 2025October 30, 2025 Post navigation Previous Post A taxonomy of C++ types Leave a comment Cancel reply [ ] [ ] [ ] [ ] [ ] [ ] [ ] D[ ] Feeds * RSS - Posts * RSS - Comments Search for: [ ] [Search] Recent Comments Unknown's avatar A prvalue is not a t... on lvalues, rvalues, glvalues, pr... Anders Schau Knatten's Anders Schau Knatten on CppQuiz Android App avatar now a... Unknown's avatar Anonymous on CppQuiz Android App now a... A Taxonomy of C++ Ty... on A taxonomy of C++ types C++ Lei Xing Fen Lei Xue - Pian Zhi De Ma Nong on A taxonomy of C++ types Top Posts & Pages * A prvalue is not a temporary * lvalues, rvalues, glvalues, prvalues, xvalues, help! * The Order of #include Directives Matter * Microsoft C++ versions explained * How to avoid includes in headers * Undefined Behaviour -- Worse Than its Reputation? * A taxonomy of C++ types * The difference between no move constructor and a deleted move constructor * Why we probably shouldn't have constexpr conditional operator * Be Careful with Static Variables Archives * October 2025 * April 2025 * March 2025 * November 2024 * September 2024 * June 2024 * March 2023 * January 2023 * August 2022 * October 2021 * May 2019 * October 2018 * September 2018 * July 2018 * April 2018 * March 2018 * July 2016 * June 2014 * May 2014 * December 2013 * November 2013 * October 2013 * April 2013 * November 2012 * October 2012 * September 2012 * August 2012 * June 2012 * May 2012 * April 2012 * March 2012 * November 2011 * October 2011 * August 2011 * April 2011 * March 2011 * January 2011 * November 2010 * October 2010 * September 2010 * August 2010 * July 2010 * June 2010 * May 2010 * April 2010 * March 2010 by Anders Schau Knatten Search for: [ ] [Search] Recent Comments Unknown's avatar A prvalue is not a t... on lvalues, rvalues, glvalues, pr... Anders Schau Knatten's Anders Schau Knatten on CppQuiz Android App avatar now a... Unknown's avatar Anonymous on CppQuiz Android App now a... A Taxonomy of C++ Ty... on A taxonomy of C++ types C++ Lei Xing Fen Lei Xue - Pian Zhi De Ma Nong on A taxonomy of C++ types Top Posts * A prvalue is not a temporary * lvalues, rvalues, glvalues, prvalues, xvalues, help! * The Order of #include Directives Matter * Microsoft C++ versions explained * How to avoid includes in headers * Undefined Behaviour -- Worse Than its Reputation? * A taxonomy of C++ types * The difference between no move constructor and a deleted move constructor * Why we probably shouldn't have constexpr conditional operator * Be Careful with Static Variables Archive * October 2025 (1) * April 2025 (1) * March 2025 (1) * November 2024 (1) * September 2024 (2) * June 2024 (2) * March 2023 (1) * January 2023 (1) * August 2022 (1) * October 2021 (1) * May 2019 (1) * October 2018 (3) * September 2018 (4) * July 2018 (1) * April 2018 (1) * March 2018 (2) * July 2016 (1) * June 2014 (1) * May 2014 (1) * December 2013 (1) * November 2013 (1) * October 2013 (1) * April 2013 (1) * November 2012 (3) * October 2012 (1) * September 2012 (5) * August 2012 (1) * June 2012 (1) * May 2012 (2) * April 2012 (1) * March 2012 (1) * November 2011 (2) * October 2011 (1) * August 2011 (4) * April 2011 (1) * March 2011 (1) * January 2011 (1) * November 2010 (1) * October 2010 (5) * September 2010 (3) * August 2010 (4) * July 2010 (4) * June 2010 (4) * May 2010 (5) * April 2010 (6) * March 2010 (5) Blog at WordPress.com. * Comment * Reblog * Subscribe Subscribed + [croppe] C++ on a Friday Join 81 other subscribers [ ] Sign me up + Already have a WordPress.com account? Log in now. * + [croppe] C++ on a Friday + Subscribe Subscribed + Sign up + Log in + Copy shortlink + Report this content + View post in Reader + Manage subscriptions + Collapse this bar %d [b]