在构造 std::variant 时禁用从指针类型到 bool 的隐式转换的最佳方法是什么?

jwe*_*rek 8 c++ visual-studio 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
}
Run Code Online (Sandbox Code Playgroud)

正如评论中所讨论的,我相信以上是合法的 C++17。有一个提案P0608R3已被标准接受,以解决这种令人惊讶的行为,但它在 2018 年(在圣地亚哥会议上)被接受,因此适用于 C++20 而不是 C++17。此外,P0608R3 当前未在 Visual Studio 中实现,即使在编译到 C++20 预览版时也是如此。

从指向非 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
}
Run Code Online (Sandbox Code Playgroud)

我错过了一些更简单的方法吗?

jwe*_*rek 0

我认为处理隐式转换的最干净方法是在变体方面做令人惊讶的事情,如果 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.
}
Run Code Online (Sandbox Code Playgroud)