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