Discussion: A Strange Assignment
Execute the code to understand the output and gain insights into the concept of temporary materialization.
Run the code
Now, it’s time to execute the code and observe the output.
#include <iostream>#include <string>std::string getName(){return "Alice";}int main(){std::string name{"Bob"};getName() = name;std::cout << "Assigned to a function!\n";}
Understanding the output
Wait, what? You can assign to a function? What does that even mean?
At first glance, it looks like we’re assigning a variable to a function call, which is the opposite of what we typically see (name = getName()
). Let’s have a closer look at what’s going on.
The function assignment
The getName()
expression calls getName
, which returns a string "Alice"
. This results in a temporary object of type std::string
with the value "Alice"
. Then, we copy-assign the variable name
(which contains the value "Bob"
) into this temporary object. When we look at it this way, it suddenly makes sense. But a closer look raises some questions.
Comparison with the previous puzzle
In the Counting Copies puzzle, we were introduced to prvalues:
Resource getResource() { return Resource{}; }Resource resource1 = getResource();
There, we discussed that the getResource()
function call is a prvalue, which does not result in a temporary Resource
. Instead, the return value is created directly in the resource1
object, and no copy is allowed to be made.
The getName()
expression in this puzzle is also a prvalue, so the returned object doesn’t even exist until we assign it to something, which we don’t do!
Discarded-value expression
If no object exists, how can we then copy-assign name
to it? Temporary materialization to the rescue. The reason that function calls are prvalues and don’t create objects at all is to avoid unnecessary copies. In the preceding Resource
example, the creation of an actual object is delayed until we assign the result of getResource()
to resource1
. But sometimes, we call a function just for its side effects and don’t assign the result to anything, like when we call getName()
in this puzzle. When we don’t assign the result of an expression to anything, we call it a discarded-value expression. We still want an object to be created, so a temporary object is materialized. That’s what happens here—the prvalue expression getName()
creates a temporary std::string
.
Now that we have an actual std::string
temporary, we can copy-assign name
to it just fine.
Binding references to prvalues
Another case where temporary materialization is important is when we bind a reference to a prvalue. Imagine, for instance, we had a function like one of these:
void useName(const std::string& name_ref);void useName(std::string&& name_ref);
If we then call useName(getName());
, the reference parameters name_ref
would need to have actual objects to bind to. And that’s indeed what happens when we bind a reference to prvalue—a temporary is materialized.
Level up your interview prep. Join Educative to access 70+ hands-on prep courses.