问题描述
请考虑以下内容:
struct foo {
};
struct bar {
};
int main()
{
foo f;
bar b;
std::variant<foo*,bool> v;
v = &b; // compiles in Visual Studio 19 v16.7.3
}
正如评论中所讨论的,我相信以上内容是合法的C ++ 17。有一项提案P0608R3已被接受来解决这种令人惊讶的行为的标准,但该提案于2018年(在圣地亚哥会议上)被接受,因此适用于C ++ 20而非C ++ 17 。即使在编译为C ++ 20预览版时,Visual Studio当前仍未实现P0608R3。
从指向非foo的指针创建此变体的最佳/最少冗长方法是编译时错误? 我相信以下内容可以工作,但如果该变体包含多个项目,那么它会很多。
struct foo {
};
struct bar {
};
using variant_type = std::variant<foo*,bool>;
struct var_wrapper : public variant_type
{
var_wrapper(foo* v = nullptr) : variant_type(v)
{}
var_wrapper(bool v) : variant_type(v)
{}
template<typename T>
var_wrapper(T*) = delete;
};
int main()
{
foo f;
bar b;
var_wrapper vw;
vw = &f; // fine
vw = true; // fine
vw = &b; // compile time error
}
我想念一些简单的方法吗?
解决方法
另一种解决方案是引入另一个bool
包装器,该包装器除了bool
以外,什么都不能构造:
#include <variant>
struct foo {};
struct bar {};
struct StrongBool {
bool value = false;
StrongBool() noexcept = default;
StrongBool(bool b) noexcept : value(b) {}
template<class T>
StrongBool(T) = delete;
};
int main() {
foo f;
bar b;
std::variant<foo*,StrongBool> v;
v = true;
v = &f;
v = &b; // fails to compile
}
无论如何,限制可接受的初始化程序都需要使用用户定义的构造函数引入类型包装器。
,我认为,如果std :: variant的行为有问题,则处理隐式转换的最干净方法是针对变体进行令人惊讶的事情,即使用“强变体”类型。也就是说,实施一种变体类型,该变体类型仅使用与变体中的类型完全相同的类型来强制构建。
使用std :: disjunction可以很容易地测试类型是否是C ++ 17中参数包的成员,从而实现如下所示:
template<typename... Ts>
class strong_variant : public std::variant<Ts...>
{
public:
template <typename T,typename U =
typename std::enable_if<std::disjunction_v<std::is_same<T,Ts>...>>::type>
strong_variant(T v) : std::variant<Ts...>(v)
{}
strong_variant() : std::variant<Ts...>()
{}
};
struct foo {};
struct bar {};
int main()
{
foo f;
bar b;
const foo c_f;
strong_variant<foo*,std::string,bool> sv;
sv = &f; // okay.
sv = true; // okay.
sv = "foo"s; // okay.
sv = "foo"; //no,must a string.
sv = &b; // no,must be a foo.
sv = &c_f; // no,must be non-const.
}