3

在分配给变体时,我觉得我缺少一些关于 int 类型提升的明显内容。

在 gcc 版本 9.3.0(Ubuntu 9.3.0-11ubuntu0~18.04.1)上,使用 -std=c++17 编译,以下代码编译失败:

#include <variant>
#include <iostream>

int main()
    {
    std::variant<long int, bool> v;  // works fine if "long" is omitted
    long int sanity = 1;             // verify that we can assign 1 to a long int; works fine

    std::cout << sizeof(sanity) << "\n";

    v = 1;                           // compiler error here: why doesn't this assign to the long int variant of v?

    return 0;
    }

错误信息:

error: no match for ‘operator=’ (operand types are ‘std::variant<long int, bool>’ and ‘int’)

是否有任何魔法可以让这项工作按预期进行,而不需要在作业中进行显式转换?谢谢!

4

2 回答 2

7

分配给变体并不是简单地分配给变体中当前活动的类型。相反,右手边的类型用于确定哪些可能的类型(替代品)是右手边的最佳匹配。然后,该类型被分配给。

因此,v = 1;不会自动分配给long int已经在里面的值v,而是进行编译时计算以确定是否long int更好bool地匹配int(右侧的类型)。最佳匹配是使用重载解决规则确定的。换句话说,我们必须想象存在两个函数

void f(long int);
void f(bool);

并问自己将调用哪个函数f(1)。如果long int选择了重载,则v = 1分配给long int对象。如果bool选择了重载,long int则当前在其中v的 将被销毁,并bool使用值 1 构造一个新对象。

不幸的是,这种重载解决方案是模棱两可的:1需要“积分转换”来匹配long intor bool。因此,分配 1v是编译时错误。如果备选方案是intbool,那么int备选方案将产生完全匹配并且不会有歧义。

这个特定的例子在 C++20 中是固定的:替代方案被排除在考虑之外,因为从一个值初始化一个值bool需要缩小转换。因此,在 C++20 中,此代码将始终分配给替代项(如果当前在变体中存在对象,则将其销毁)。boolintlong intbool

于 2021-04-13T01:30:45.513 回答
3

从 int 转换为这两种类型具有相同的“距离”。

它不知道您要分配给哪个。

您可以创建一个拒绝从 int 转换的非 bool 布尔值。

struct boolean{
  bool value=false;
  constexpr boolean()=default;
  template<class T, std::enable_if_t<std::is_integral<T>{}, bool> =true>
  boolean(T)=delete;
  constexpr boolean(bool b):value(b){}
  constexpr boolean(boolean const&)=default;
  constexpr boolean& operator=(boolean const&)=default;
  constexpr explicit operator bool()const{return value;}
  constexpr friend bool operator==(boolean lhs, boolean rhs) { return lhs.value==rhs.value; }
  constexpr friend bool operator!=(boolean lhs, boolean rhs) { return lhs.value!=rhs.value; }
};

也许更多的操作。

然后variant<boolean, long int>不会从int.

活生生的例子

于 2021-04-13T01:36:31.097 回答