Reputation: 1
I have the following C++ code that returns a std::tuple<std::vector<int>, std::vector<int>>
. My question is about copy elision and named return value optimisation. As I understand, since C++-17, the standard mandates that the return value will be copy-elided. So no temporary tuple will be created and then, (I assume) std::tie
will directly initialise list1, list2
. Now, whether it will call a copy or move constructor for the vectors or allocate space before function call and directly use that space depends on whether NRVO is performed. I would like to know whether NRVO is performed in this case, and if not, how I can change the code to achieve a single vector allocation for both lists (without passing them in as parameters).
std::tuple<std::vector<int>, std::vector<int>> parseInput(const std::string &filename) {
std::ifstream file(filename);
if (!file.is_open()) {
std::cerr << "Error: could not open file.\n";
std::exit(1);
}
std::vector<int> list1, list2;
int num1, num2;
std::string line;
while (std::getline(file, line)) {
std::istringstream iss(line);
if (iss >> num1) {
if (iss >> num2) {
list1.push_back(num1);
list2.push_back(num2);
} else {
std::cerr << "Error: Line only contains one number. Exiting.\n";
std::exit(1);
}
} else {
std::cerr << "Warning: Line contains no numbers. Skipping.\n";
}
}
return {list1, list2};
}
int main(int argc, char *argv[]) {
...
std::vector<int> list1, list2;
std::tie(list1, list2) = parseInput(f);
...
}
I tried to view this in Compiler Explorer (https://godbolt.org/z/oxP1z5c3a). As far as I can tell, neither copy elision nor NRVO is performed because the code for parseInput
calls both vector
and tuple
constructors. I would like to understand why better, and know how to improve my code.
EDIT: The question was a bit unclear. There are two objects of interest here - the tuple
and the vector
. In my example, the tuple was only being allocated once because of RVO. But the vectors were being allocated twice and copied. It is possible to convert the move to a copy by replacing the return {list1, list2};
with return { std::move(list1), std::move(list2) }
(suggested by @Jarod42). Another option (suggested by @Eljay) is to created a named tuple t
in the beginning and return t
. Here, NRVO will be performed and both the tuple and the vectors will only be allocated once. Here is a cleaned-up example showing this.
Upvotes: 0
Views: 112