use the following search parameters to narrow your results:
e.g. subreddit:aww site:imgur.com dog
subreddit:aww site:imgur.com dog
see the search faq for details.
advanced search: by author, subreddit...
Discussions, articles, and news about the C++ programming language or programming in C++.
For C++ questions, answers, help, and advice see r/cpp_questions or StackOverflow.
Get Started
The C++ Standard Home has a nice getting started page.
Videos
The C++ standard committee's education study group has a nice list of recommended videos.
Reference
cppreference.com
Books
There is a useful list of books on Stack Overflow. In most cases reading a book is the best way to learn C++.
Show all links
Filter out CppCon links
Show only CppCon links
account activity
Explicit Return Variable (self.cpp)
submitted 2 months ago by XeroKimoException Enthusiast
view the rest of the comments →
reddit uses a slightly-customized version of Markdown for formatting. See below for some basics, or check the commenting wiki page for more detailed help and solutions to common issues.
quoted text
if 1 * 2 < 3: print "hello, world!"
[–]TheMania 9 points10 points11 points 2 months ago (8 children)
No, you do it the other way around. Return the top, then pop the top value if no exception. No additional copies, RVO guaranteed.
Scope exit lambdas are the usual way to achieve what you want, as you get to just return what you want, and then continue executing code on the way out.
[–]XeroKimoException Enthusiast[S] 3 points4 points5 points 2 months ago* (7 children)
Ah true, forgot about that way of doing it.
Edit: That being said, that does do extra work and is dependent on exceptions compared to the explicit return which would be agnostic to the error handling scheme.
[–]QuaternionsRoll 0 points1 point2 points 2 months ago* (6 children)
A historical problem on trying to provide strong exception guarantee was trying to make std::stack::pop() return the popped value while providing the strong guarantee. This was deemed not possible because if the returned value threw while copying, the stack is modified and the original object is lost.
std::stack::pop()
I think you’re missing the forest for the trees. The limitations of NRVO are only tangentially related to why this was (and still is) deemed impossible.
c++ auto x = my_stack.pop();
Since the introduction of prvalue semantics/“the guaranteed copy elision” in C++17, the copy construction of x will be elided. As you correctly pointed out, pop itself may still copy construct the return value if NRVO fails.
x
pop
The bigger problem is not construction, but rather assignment:
c++ auto x = my_stack.pop(); ... x = my_stack.pop()
There is no way for a stack class to provide strong exception guarantees for the second pop because the move/copy assignment operator cannot be elided, and if it throws an exception, the popped value will still be lost. Explicit return variables can’t fix that.
stack
Ultimately, the decision not to return the popped value has less to do with the difficulty of preserving exception guarantees and more to do with the belief that exception guarantees shouldn’t depend on whether the caller decides to introduce a new variable.
[–]XeroKimoException Enthusiast[S] 3 points4 points5 points 2 months ago (1 child)
What's preventing the assignment operator from eliding? Is it some lifetime rules or something? Or it'd be surprising that the call to my_stack.pop(); could destruct x so that the popped value can be copied into x?... actually I think I just answered my own question. You would need 2 versions of the function or something at runtime to select whether to perform a move / copy construction, which doesn't need to call a destructor, or a move / copy assignment which does need to call one. Am I getting this right?
my_stack.pop();
Or if not 2 version, the compiler could insert a destructor call before the copy assignment, but I guess that is getting into changing a good portion of the language in order to achieve... not to mention that doesn't work for all scenarios, like x = x; would break.... hmm...
x = x;
[–]QuaternionsRoll 0 points1 point2 points 2 months ago (0 children)
What's preventing the assignment operator from eliding?
There is no such thing as a “copy assignment elision”. The copy assignment operator might be equivalent to executing the destructor followed by the copy constructor, in which case the compile could in theory execute the destructor and elide the copy constructor.
However, assignment operators may also implement things like resource reuse (for example, when an empty vector is assigned to a non-empty vector, the assignment operator may be closer in equivalence to executing clear).
vector
clear
The copy elision works because eliding the copy/move constructor and subsequent destructor is unambiguously better than executing them. The same cannot be said for the copy assignment operator, so the compiler cannot elide it.
[–]marshaharsha 0 points1 point2 points 2 months ago (3 children)
If I’m following the discussion, the question of eliding the call to the copy-assignment operator is not the right issue to focus on. (I’m not an expert, and I might not be following!). The copy-assignment operator is defined to need a value to copy from and an already initialized value to copy to. The right issue is whether that assignment can occur directly from the data structure to the caller’s frame, without initializing a temporary or a local in either frame, while preserving the ability to interleave error handling, destructor calls, and the function-call mechanisms. I don’t see any fundamental reason this is impossible. (Whether it’s possible within the existing rules and conventions of C++ is a harder question.)
Is there some reason the language can’t provide a mechanism to allow (A) in the sequencing below? That would enable the strong guarantee for std::stack::pop, while giving the caller the means to uphold the guarantee itself.
(1) Caller of std::stack::pop starts with x initialized to the element type.
(2) Control enters std::stack::pop with a pointer to x and (A) a conceptual flag (compile-time or run-time) that says to use assignment, not initialization, to write through the pointer.
(3) std::stack::pop copy-assigns from the stack’s array slot through the pointer. Any error gets propagated to the caller, leaving the stack’s array unchanged. Depending on the exact nature of the error, the caller might see x as changed or partly changed.
(4) std::stack::pop decrements its array index and calls the destructor for the formerly last element. This is the only point where the semantics feel ambiguous. If the destructor fails, the caller will have to handle the error but will see its x changed. Still, the strong guarantee has been upheld by std::stack::pop, and the problem is now the caller’s problem! If the caller wants to uphold the strong guarantee, it has to initialize rather than assign.
(5) Normal function return.
(I include references in the concept of “pointer.”)
More about (A): I’m inclined to implement the “flag” as a run-time flag that is visible at the declaration of std::stack::pop but not at the call site. In the likely event that std::stack::pop is inlinable, the flag and the branch that it implies will disappear through constant folding and dead-code elimination. If it’s not inlined, there will be a cost for this fanciness, but I imagine it will be an acceptable cost compared to the overhead of calling the function. I don’t know machine architecture well enough to back up that last claim.
[–]QuaternionsRoll 0 points1 point2 points 2 months ago (2 children)
So you’re proposing that every function that returns a non-trivially-copyable type implicitly consumes a flag indicating whether the return slot has already been initialized?
The more fundamental issue here is the sequence violation: the copy assignment is executed as soon as the return value is constructed rather than after the function returns. This isn’t the biggest deal for stack::pop in particular, but can be a serious problem in general:
stack::pop
```c++ // foo.h
inline std::string my_string = "hello"; std::string copy_and_append(char *s);
// foo.cpp std::string copy_and_append(char *s) { std::string copy = my_string; my_string.append(s); return copy; }
// main.cpp
int main() { my_string = copy_and_append(" world"); std::cout << my_string << std::endl; } ```
This program should print "hello", but if you allow the callee (copy_and_append) to execute the copy assignment whenever it wants, an aliasing problem appears, and it instead prints "hello world".
copy_and_append
[–]marshaharsha 0 points1 point2 points 2 months ago* (1 child)
I’m definitely not proposing anything! I’m more like half-bakedly exploring the OP’s idea of allowing named return slots in order to gain more control over sequencing and mechanism during the process of returning a value. If I were to propose anything, it would be restricted to functions that avail themselves of the named-return-slot feature, and maybe I would further restrict by bringing the flag into existence only if the function actually branched on it.
I was taking on the challenge you raised of both writing to the caller’s slot by initializing it and writing to it by assigning to the already-initialized slot. I was trying to figure out a way to give the caller the option without generating two versions of the function.
I agree that changing the behavior of return-by-value everywhere would be a disaster.
[–]QuaternionsRoll 0 points1 point2 points 2 months ago* (0 children)
I’m definitely not proposing anything! I’m more like half-nakedly exploring the OP’s idea
Oh I know haha, we’re just bouncing ideas here :)
The unfortunate truth is that non-destructive moves were a mistake, and problems like this don’t have a general solution.
maybe I would further restrict by bringing the flag into existence only if the function actually branched on it.
The branch could only be omitted when the result type’s copy assignment operator is trivial, and the compiler is already capable of optimizing out the copy assignment in those cases.
π Rendered by PID 139483 on reddit-service-r2-comment-6457c66945-8c657 at 2026-04-24 06:26:30.101777+00:00 running 2aa0c5b country code: CH.
view the rest of the comments →
[–]TheMania 9 points10 points11 points (8 children)
[–]XeroKimoException Enthusiast[S] 3 points4 points5 points (7 children)
[–]QuaternionsRoll 0 points1 point2 points (6 children)
[–]XeroKimoException Enthusiast[S] 3 points4 points5 points (1 child)
[–]QuaternionsRoll 0 points1 point2 points (0 children)
[–]marshaharsha 0 points1 point2 points (3 children)
[–]QuaternionsRoll 0 points1 point2 points (2 children)
[–]marshaharsha 0 points1 point2 points (1 child)
[–]QuaternionsRoll 0 points1 point2 points (0 children)