Search⌘ K
AI Features

Pass by Value When Applicable

Explore the technique of passing arguments by value in C++, including how it leverages move semantics to avoid unnecessary copying in some cases. Understand scenarios where pass-by-value is optimal, such as constructor parameters, and cases where it can lead to performance costs, particularly when dealing with objects having internal buffers. Learn when to provide separate overloads for lvalues and rvalues to maximize efficiency.

Passing by value: when and why to use it

Consider a function that converts a std::string to lowercase. In order to use the move-constructor where applicable, and the copy-constructor otherwise, it may seem like two functions are required:

C++ 17
// Argument s is a const reference
auto str_to_lower(const std::string& s) -> std::string {
auto clone = s;
for (auto& c: clone) c = std::tolower(c);
return clone;
}
// Argument s is an rvalue reference
auto str_to_lower(std::string&& s) -> std::string {
for (auto& c: s) c = std::tolower(c);
return s;
}
TEST(MoveSemantics, StrToLower) {
std::cout << "tolower\n";
auto upper = std::string{"ABC"};
auto lower = str_to_lower(std::move(upper));
ASSERT_TRUE(upper.empty());
ASSERT_FALSE(lower.empty());
}

However, by taking the std::string by value instead, we can write one function that covers both cases:

C++ 17
auto str_to_lower(std::string s) -> std::string {
for (auto& c: s) c = std::tolower(c);
return s;
}

Let's see why this implementation of str_to_lower() avoids unnecessary copying where possible. When passed a regular variable, shown as follows, the content of str is copy-constructed into ...