我从这样的场景开始:
Foo1
、Foo2
和 Baz
是不可触及的
struct 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
Bar
,我可以控制,最初看起来像这样:
struct Bar {
Bar(int i)
: i{i}
, foo{i}
, baz{foo}
{}
int i;
Foo1 foo;
Baz baz;
};
稍后,需要
Bar::foo
实际上是 Foo1
或 Foo2
,具体取决于馈送到 i
构造函数的 Bar
。
我最初的想法是使用
std::variant
,但是更改 Bar
来满足新要求并不完全简单,因为 Foo1
、Foo2
和 Baz
的不可复制或可移动性
,妨碍:
Foo1 foo;
更改为 std::variant<Foo1, Foo2> foo;
会导致初始化 foo{i}
无法编译,这是正常的,但我们也简化一下,现在我们仍然只在 Foo1
中放置 foo
,将 foo{i}
更改为 foo{Foo1{i}}
;这仍然不起作用,因为 Foo1
;Foo1
中构建 std::variant
;如果 Bar
没有 Baz
成员,这将是可行的,因为我可以通过在其中包含 std::variant
来使 std::monostate
默认可构造(这也将允许有条件构造 Foo1
或 Foo2
:
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;
};
上述观察使我认为解决方案可以使用
std::unique_ptr
,但它也需要相当多的代码,因为必须std::visit
才能构造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;
};
我想知道我是否错过了一些更干净的方法来处理这种情况。
您可以构建变体,无需
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;
};
演示 不幸的是,msvc 拒绝该代码,但使用中间函数可以。