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

Posted

技术标签:

【中文标题】在构造 std::variant 时禁用从指针类型到 bool 的隐式转换的最佳方法是啥?【英文标题】:What is the best way to disable implicit conversion from pointer types to bool when constructing an std::variant?在构造 std::variant 时禁用从指针类型到 bool 的隐式转换的最佳方法是什么? 【发布时间】:2020-10-07 18:03:55 【问题描述】:

考虑以下几点:

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

正如 cmets 中所讨论的,我相信以上是合法的 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

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

【问题讨论】:

在我看来像 error。 这对我来说似乎是一个错误。 cppreference 表示如果类型列表包含一个可能有 cv 限定的bool,那么只有当它的参数恰好是一个可能有 cv 限定的bool 时,该类型才应该由operator= 激活。也就是说,不应进行隐式转换。 @jwezorek 根据docs.microsoft.com/en-us/cpp/overview/…,这个 (P0608R3) 还没有在 MSVC 标准库中实现。 恐怕你是对的。 【参考方案1】:

另一种解决方案是引入另一个 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
 

无论如何,限制可接受的初始化器需要引入带有用户定义构造函数的类型包装器。

【讨论】:

【参考方案2】:

如果 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.

【讨论】:

很高兴您得到了解决方案,感谢您的分享,如果您将它们标记为答案,我将不胜感激,这将对其他社区有益。

以上是关于在构造 std::variant 时禁用从指针类型到 bool 的隐式转换的最佳方法是啥?的主要内容,如果未能解决你的问题,请参考以下文章

在 std::variant 中使用不完整类型

使用类作为数据类型时如何在 std::variant 中存储值?

如何在编译时捕获 std::variant 持有错误类型?

std::visit 无法推断 std::variant 的类型

std::get_if (std::variant) 通过指针而不是值/&/const& 获取变体参数是不是有任何实际原因?

具有 std::map 和 std::variant 的不完整类型