显式转换运算符vs get<对于std :: variant

时间:2016-09-19 10:17:27

标签: c++ variant c++17 conversion-operator

除了(或甚至代替)std::variant自由函数之外,使get< T >()类的一部分成为显式转换(替代)运算符重载的缺点是什么(p.568 draft)?

template<class... Types>
class variant
{
    template<class T>
    explicit operator const T& () const&;
    template<class T>
    explicit operator T& () &;
    template<class T>
    explicit operator const T&& () const &&;
    template<class T>
    explicit operator T&& () &&;
};

在某些情况下它是不安全的吗?为什么我们需要免费的get()函数(即&#34;类型&#34;版本,而不是&#34;索引&#34;一个),当variant公开一个值语义时(引用草案):< / p>

  

变体对象保存并管理值的生命周期。

是否足以使变种明确地转换为替代品,但仍然可以从替代品中隐式构建?

我知道,界面的一致性是好事(我记得我们需要get< I >()get< T >()),但我认为仅仅通过转换为更自然的方式来包含替代值它,而不是将一些函数专门化应用于变体实例。

3 个答案:

答案 0 :(得分:3)

我可以想到一些缺点:

  • std::variant可以包含 cv void,但转换函数会返回 cv void& cv void&&将是非法的,并且永远不会调用 cv void的转换函数( [class.conv.fct] / 1);
  • std::variant可以保存一个也可以从中构造的类型,例如:std::variant<std::monostate, std::any> v; std::any a{v}; - 在这种情况下会发生什么?

此外,目前可以使用返回T的函数,将其转换为返回std::variant<T, U>的函数,并期望编译器检测需要更改代码的所有情况;转换函数为T任何代码复制或绑定对T的引用的情况都会导致错误:

int f();
int i{f()};    // OK

// f() changes to:
std::variant<int, std::string> f();
int i{f()};    // can now throw std::bad_variant_access

答案 1 :(得分:1)

1)如果std::variant只有一个案例,我认为有一个案例是无争议的。 (这可能发生在通用代码中。)然后变量应该(隐式地,IMO)可转换为该单一类型。

std::variant<double> v(5.3);
...
double d; d = v; // should be ok

2)对于其他非退化情况,我认为它应该可以转换(显式),就像std::get那样。 我只会针对原始 variant的情况(而不是任意类型)执行此操作。

std::variant<double, int> v(5.3);
...
double d{v}; // should be ok, but can throw

我不认为因void案例而没有这个功能的借口是好的。 在最坏的情况下,void是问题,而不是variantvoid个案件应由monostate处理(或将来由“常规void”处理。

总而言之,我支持你的想法,除了我会对转换运算符施加限制。

另外,我会返回一个副本而不是一个引用(绝对不是非常量引用)。 原因是,通过引用,可以将变体内容置于无效状态(如原始联合所发生的那样),或者如果变体稍后更改,则引用本身可能处于无效状态。

[编辑:我把它拿回去,引用一直无效,这没关系,例如,如果你引用了一个向量的元素,并且稍后调整了向量。]

template<class... Types>
class variant
{
    template<class T>
    explicit operator T&() const{return std::get<T>(*this);}
    ...
};

template<class T>
class variant
{
    /*implicit*/ operator T&() const{return std::get<T>(*this);}
    ...
};

由于缺少该功能,一些临时解决方法

一个。派生自std::variant并添加这些功能。从变体中衍生或聚集似乎是一种常见的模式。

湾变体案例的重载函数,因此它可以直接使用变体并立即调用std::get<type>(...)上的原始函数。

答案 2 :(得分:0)

我不提出建议的主要原因如下:

  • 可选没有它
  • 是否有explicit operator bool()has_value()或不?)
  • 变体和可选项应尽可能保持一致。

但鉴于正确的论据,这肯定会在未来的修订中改变。