Reputation: 448
I know that std::optional<T&>
isn't supported in the standard. This question is about whether passing std::optional<T>&
has any performance advantage
Sample code (https://godbolt.org/z/h56Pj6d6z) reproduced here
#include <ctime>
#include <iomanip>
#include <iostream>
#include <optional>
void DoStuff(std::optional<std::string> str) {
if (str) std::cout << "cop: " << *str << std::endl;
}
void DoStuffRef(const std::optional<std::string>& str) {
if (str) std::cout << "ref: " << *str << std::endl;
}
int main() {
std::optional<std::string> str = {};
DoStuff(str);
DoStuffRef(str);
str = "0123456789012345678901234567890123456789";
DoStuff(str);
DoStuffRef(str);
}
(My actual use case is an optional for a complex user-defined type, but I hope that a long string would do the same compiler-wise)
In this case, does DoStuffRef
actually save any copying effort compared to DoStuff
?
I tried to look at godbolt output but I don't know enough assembly to be sure. I do see that in the case of DoStuff
, there seems to be a temp std::optional<T>
created which is not present in DoStuffRef
so my suspicion is that yes, passing an optional
by reference save some copying
Appreciate the help!
Upvotes: 10
Views: 8019
Reputation: 218138
DoStuffRef
saves an extra copy when argument is already std::optional<std::string>
(as in your example).
But, if you pass directly a std::string
, then in both cases, a std::optional
should be constructed, involving copy/move constructor of the string.
Upvotes: 6
Reputation: 817
Passing std::optional<std::string>
by value as in DoStuff
will be slower any way since the std::optional<> represents the object and according to definition
Any instance of optional<T> at any given point in time either contains a value or
does not contain a value.
If an optional<T> contains a value, the value is guaranteed to be allocated as part
of the optional object footprint
The key here is the optional object footprint
term
Let's say an emtpy std::string occupies 16 bytes - then optional for empty string will take at least 16 bytes. And passing optional by value will lead to 16 bytes copy all the time (excluding use case when compiler can pass it as rvalue & apply move semantic to entire optional object instance)
Take a look at representative benchmark for the provided use case:
std::optional<std::string> str = {};
DoStuff(str); // pass optional<> without value
DoStuffRef(str); // still 2 times faster than above call
std::optional<std::string> str = {""};
DoStuff(str); // pass optional<> with value - empty string
DoStuffRef(str); // still 7 times faster than above call
str = "0123456789012345678901234567890123456789";
DoStuff(str); // pass optional<> with big string
DoStuffRef(str); // 60X times faster than above call
Upvotes: 5
Reputation: 13589
If you pass actual std::optional<std::string>
then yes, there would be no copy. But if you pass just std::string
then temporary optional has to be constructed first, resulting in a copy of the string.
Upvotes: 7