在构造 std::variant 时禁用从指针类型到 bool 的隐式转换的最佳方法是什么?
What is the best way to disable implicit conversion from pointer types to bool when constructing an std::variant?
考虑以下几点:
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 .此外,P0608R3 当前未在 Visual Studio 中实现,即使在编译为 C++20 预览版时也是如此。
从指向 non-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
}
无论如何,限制可接受的初始值设定项需要引入带有 user-defined 构造函数的类型包装器。
我认为,如果 std::variant 的行为是一个问题,处理隐式转换的最简洁的方法就是使用“强变体”类型。即,实现一个变体类型,该变体类型仅使用完全变体中类型的类型来强制构造。
在 C++17 中使用 std::disjunction 很容易测试一个类型是否是参数包的成员,导致如下实现:
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.
}
考虑以下几点:
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 .此外,P0608R3 当前未在 Visual Studio 中实现,即使在编译为 C++20 预览版时也是如此。
从指向 non-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
}
无论如何,限制可接受的初始值设定项需要引入带有 user-defined 构造函数的类型包装器。
我认为,如果 std::variant 的行为是一个问题,处理隐式转换的最简洁的方法就是使用“强变体”类型。即,实现一个变体类型,该变体类型仅使用完全变体中类型的类型来强制构造。
在 C++17 中使用 std::disjunction 很容易测试一个类型是否是参数包的成员,导致如下实现:
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.
}