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.

Press + to interact
#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.