I start from a scenario like this:
- the non-copiable-nor-movable classes
Foo1
,Foo2
, andBaz
are untouchablestruct Foo1 { Foo1(int); Foo1(Foo1&&) = delete; }; // I can't touch it struct Foo2 { Foo2(int); Foo2(Foo2&&) = delete; }; // I can't touch it struct Baz { Baz(Foo1&); Baz(Foo2&); Baz(Baz&&) = delete; }; // I can't touch it
- a fourth class,
Bar
, I have control of, looks initially like this:struct Bar { Bar(int i) : i{i} , foo{i} , baz{foo} {} int i; Foo1 foo; Baz baz; };
At a later point, the need arises that Bar::foo
be actually either a Foo1
or a Foo2
depending on the i
fed to Bar
‘s constructor.
My initial thought was to use a std::variant
, but changing Bar
to fulfil the new requirement is not entirely straightforward, because the non-copiable-nor-movable-ity of Foo1
, Foo2
, and Baz
, gets in the way:
- changing
Foo1 foo;
tostd::variant<Foo1, Foo2> foo;
causes in the initializationfoo{i}
to fail to compile, and that’s normal, but let’s also simplify things by saying that for now we’re still only putting aFoo1
infoo
, by changing alsofoo{i}
tofoo{Foo1{i}}
; this still doesn’t work because of the non-movability ofFoo1
; - so I think, ok, I need to construct the
Foo1
in place in thestd::variant
; this would be viable, ifBar
did not have aBaz
member, because I could make thestd::variant
default constructible by includingstd::monostate
in it (and this would also allow the conditional construction ofFoo1
orFoo2
:struct Bar { Bar(int i) : i{i} //, baz{foo} { if (i == 0) { foo.emplace<Foo1>(i); } else { foo.emplace<Foo2>(i); } } int i; std::variant<std::monostate, Foo1, Foo2> foo{}; //Baz baz; };
The above observations lead me to think that a solution could be using std::unique_ptr
, but it does too require quite a bit of code, because one must std::visit
in order to construct Baz
:
struct Bar {
using Var = std::variant<std::unique_ptr<Foo1>, std::unique_ptr<Foo2>>;
Bar(int i)
: i{i}
, foo{i == 0 ? Var{std::make_unique<Foo1>(i)} : Var{std::make_unique<Foo2>(i)}}
, baz{std::visit([](auto&& p) { return Baz{*p}; }, foo)}
{}
int i;
Var foo;
Baz baz;
};
I was wondering if I have missed some cleaner way to deal with such a scenario.
1
You can construct the variant in place, no need of std::monostate
:
struct Bar {
using Foo = std::variant<Foo1, Foo2>;
Bar(int i)
: i{i}
, foo{i == 0 ? Foo(std::in_place_type<Foo1>, i) : Foo(std::in_place_type<Foo2>, i)}
, baz{std::visit([](auto&& p) { return Baz{p}; }, foo)}
{}
int i;
std::variant<Foo1, Foo2> foo;
Baz baz;
};
Demo
Unfortunately, msvc rejects that code, but using intermediate function (as lambda) works :
struct Bar {
using Foo = std::variant<Foo1, Foo2>;
Bar(int i)
: i{i}
, foo{[](int i){
if (i == 0) {
return Foo(std::in_place_type<Foo1>, i);
} else {
return Foo(std::in_place_type<Foo2>, i);
}
}(i)}
, baz{std::visit([](auto&& p) { return Baz{p}; }, foo)}
{}
int i;
std::variant<Foo1, Foo2> foo;
Baz baz;
};
Demo