为什么将 int 分配给 std::variant<long int, ...> 会失败?
Why does assigning an int to a std::variant<long int, ...> fail?
我觉得我在分配给变体时遗漏了一些关于 int 类型提升的明显信息。
在gcc version 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’)
有没有什么神奇的方法可以让这项工作按预期进行,而不需要在作业中进行显式转换?谢谢!
分配给变体并不是简单地分配给变体中当前活动的类型。相反,右侧的类型用于确定哪些可能类型(备选方案)最适合右侧。然后,将该类型分配给。
因此,v = 1;
不会自动分配给已经在 v
中的 long int
值,而是进行编译时计算以确定是否 long int
或 bool
更适合 int
(右侧的类型)。使用重载解析规则确定最佳匹配。换句话说,我们必须假设存在两个函数
void f(long int);
void f(bool);
然后问自己 f(1)
会调用哪个函数。如果选择 long int
重载,则 v = 1
分配给 long int
对象。如果选择 bool
重载,则当前在 v
中的 long int
将被销毁,并构造一个值为 1 的新 bool
对象。
不幸的是,此重载解析不明确:1
需要“积分转换”以匹配 long int
或 bool
。因此,将 1 分配给 v
是一个编译时错误。如果备选方案是 int
和 bool
,那么 int
备选方案将产生完全匹配并且不会有歧义。
此特定示例已在 C++20 中修复:bool
替代方案已从考虑中移除,因为需要缩小转换才能从 int
初始化 bool
值价值。因此,在 C++20 中,此代码将始终分配给 long int
替代项(如果变体中当前有一个 bool
对象,它将被销毁)。
从 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
转换。
我觉得我在分配给变体时遗漏了一些关于 int 类型提升的明显信息。
在gcc version 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’)
有没有什么神奇的方法可以让这项工作按预期进行,而不需要在作业中进行显式转换?谢谢!
分配给变体并不是简单地分配给变体中当前活动的类型。相反,右侧的类型用于确定哪些可能类型(备选方案)最适合右侧。然后,将该类型分配给。
因此,v = 1;
不会自动分配给已经在 v
中的 long int
值,而是进行编译时计算以确定是否 long int
或 bool
更适合 int
(右侧的类型)。使用重载解析规则确定最佳匹配。换句话说,我们必须假设存在两个函数
void f(long int);
void f(bool);
然后问自己 f(1)
会调用哪个函数。如果选择 long int
重载,则 v = 1
分配给 long int
对象。如果选择 bool
重载,则当前在 v
中的 long int
将被销毁,并构造一个值为 1 的新 bool
对象。
不幸的是,此重载解析不明确:1
需要“积分转换”以匹配 long int
或 bool
。因此,将 1 分配给 v
是一个编译时错误。如果备选方案是 int
和 bool
,那么 int
备选方案将产生完全匹配并且不会有歧义。
此特定示例已在 C++20 中修复:bool
替代方案已从考虑中移除,因为需要缩小转换才能从 int
初始化 bool
值价值。因此,在 C++20 中,此代码将始终分配给 long int
替代项(如果变体中当前有一个 bool
对象,它将被销毁)。
从 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
转换。