std::optional and std::variant are attractive alternatives to raw pointers and unions, but they come with a caveat: when you create an optional/variant from a “real” value, a copy will be created. To avoid that, one must use reference_wrapper, and of course be careful not to end up with a dangling reference.
The following code demonstrates that copying occurs when a value is passed to a function accepting a variant:
#include <iostream>
#include <variant>
using namespace std;
struct Foo {
Foo() { cout << "Foo constructed" << endl; }
Foo(Foo const& other) { cout << "Foo copied" << endl; }
~Foo() { cout << "Foo destroyed" << endl; }
};
struct Bar {
Bar() { cout << "Bar constructed" << endl; }
Bar(Bar const& other) { cout << "Bar copied" << endl; }
~Bar() { cout << "Bar destroyed" << endl; }
};
void f(variant<Foo,Bar> foobar) {
auto const* text = holds_alternative<Foo>(foobar)
? "Foo"
: (holds_alternative<Bar>(foobar)
? "Bar"
: "unknown");
cout << "f(" << text << ")" << endl;
}
int main()
{
f(Foo{});
f(Bar{});
return 0;
}
Output:
Foo constructed Foo copied f(Foo) Foo destroyed Foo destroyed Bar constructed Bar copied f(Bar) Bar destroyed Bar destroyed
To avoid copying, one must use a reference_wrapper. Note that it is now impossible to pass a temporary:
#include <iostream>
#include <variant>
#include <functional>
using namespace std;
struct Foo {
Foo() { cout << "Foo constructed" << endl; }
Foo(Foo const& other) { cout << "Foo copied" << endl; }
~Foo() { cout << "Foo destroyed" << endl; }
};
struct Bar {
Bar() { cout << "Bar constructed" << endl; }
Bar(Bar const& other) { cout << "Bar copied" << endl; }
~Bar() { cout << "Bar destroyed" << endl; }
};
void g(variant<reference_wrapper<const Foo>,reference_wrapper<const Bar>> foobar) {
auto const* text = holds_alternative<reference_wrapper<const Foo>>(foobar)
? "Foo"
: (holds_alternative<reference_wrapper<const Bar>>(foobar)
? "Bar"
: "unknown");
cout << "g(" << text << ")" << endl;
}
int main()
{
Foo foo;
Bar bar;
g(foo);
g(bar);
return 0;
}
Output:
Foo constructed Bar constructed g(Foo) g(Bar) Bar destroyed Foo destroyed
