resnet
resnet

Reputation: 448

Does passing an std::optional<T> by reference actually save copying?

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

Answers (3)

Jarod42
Jarod42

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

N0dGrand87
N0dGrand87

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

std::optional<> pass by value vs reference

Upvotes: 5

yuri kilochek
yuri kilochek

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

Related Questions