我经常使用boost::variant
并且非常熟悉它。boost::variant
不会以任何方式限制有界类型,尤其是它们可以是引用:
#include <boost/variant.hpp>
#include <cassert>
int main() {
int x = 3;
boost::variant<int&, char&> v(x); // v can hold references
boost::get<int>(v) = 4; // manipulate x through v
assert(x == 4);
}
我有一个真实的用例,可以将引用的变体用作其他数据的视图。
然后,我惊讶地发现,它std::variant
不允许将引用作为有界类型,std::variant<int&, char&>
也无法编译,并且在这里明确指出:
不允许变体保存引用,数组或类型为void。
我想知道为什么不允许这样做,我看不到技术原因。我知道的实现std::variant
和boost::variant
不同,所以也许它做呢?还是作者认为这是不安全的?
PS:我真的不能处理限制的std::variant
使用std::reference_wrapper
,因为包装不参考不允许从基本类型分配。
#include <variant>
#include <cassert>
#include <functional>
int main() {
using int_ref = std::reference_wrapper<int>;
int x = 3;
std::variant<int_ref> v(std::ref(x)); // v can hold references
static_cast<int&>(std::get<int_ref>(v)) = 4; // manipulate x through v, extra cast needed
assert(x == 4);
}
从根本上说optional
,variant
不允许引用类型的原因是,在这种情况下应该分配什么(在较小程度上是比较)上存在分歧。optional
比variant
在示例中展示更容易,所以我坚持:
int i = 4, j = 5;
std::optional<int&> o = i;
o = j; // (*)
标记的行可以解释为:
o
,这样&*o == &j
。作为此行的结果,值i
和j
本身仍然改变。o
,&*o == &i
现在仍然如此i == 5
。分配通是您只需按下拿到的行为=
,通过对T
的=
,重新绑定是一种更完善的实现,是你真正想要的(也看到了这个问题,以及对马特·卡拉布雷斯谈话引用类型)。
解释(1)和(2)之间差异的另一种方式是我们如何在外部实现两者:
// rebind
o.emplace(j);
// assign through
if (o) {
*o = j;
} else {
o.emplace(j);
}
该Boost.Optional文档提供了这样的理由:
选择重绑定语义来分配初始化的可选引用,即使在与裸C ++引用的语义缺乏一致性的情况下,也可以提供初始化状态之间的一致性。的确,
optional<U>
无论何时初始化,都力求尽可能地表现与U一样。但在U
is 的情况下T&
,这样做会导致左值初始化状态的行为不一致。想象一下
optional<T&>
将分配转发给引用对象(从而更改引用对象的值但不重新绑定),并考虑以下代码:optional<int&> a = get(); int x = 1 ; int& rx = x ; optional<int&> b(rx); a = b ;
作业是做什么的?
如果
a
是未初始化,答案是明确的:它结合x
(我们现在有另一个参考x
)。但是如果a已经初始化怎么办?它将改变被引用对象的值(无论是什么);这与其他可能的情况不一致。如果
optional<T&>
可以像分配一样T&
进行分配,则除非显式地处理代码是否能够在赋值之后a
使用相同的对象作为别名,否则您将无法在不显式处理先前的初始化状态的情况下使用Optional的赋值b
。也就是说,您必须进行区分才能保持一致。
如果在您的代码中重新绑定到另一个对象不是一种选择,那么很可能第一次也不绑定。在这种情况下,应禁止分配给未初始化的对象
optional<T&>
。在这种情况下,很有可能必须先初始化左值。如果不是,则第一次绑定就可以,而重新绑定则不能,这在IMO中是不太可能的。在这种情况下,您可以直接分配值本身,如下所示:assert(!!opt); *opt=value;
对该行应该做什么没有达成共识,这意味着更容易完全拒绝引用,因此optional
and的大部分价值variant
至少可以使它对C ++ 17有用,并开始变得有用。引用总是可以在以后添加-否则争论就过去了。
本文收集自互联网,转载请注明来源。
如有侵权,请联系 [email protected] 删除。
我来说两句