【问题标题】:C++ safe bool wrapperC++ 安全布尔包装器
【发布时间】:2017-01-16 20:40:55
【问题描述】:

我正在尝试设计一个 bool 包装器 结构应用 safe bool idiom
解决这个问题的经典实现非常简单:骨架可能是这样的:

struct Bool final
{
  Bool() = default;

  Bool(bool value)
    : _value{value}
  {}

  explicit operator bool() const {
    return _value;
  }

private:
  bool _value{false};
};

我要改进的部分是Bool 的构造方式。
例如,我想通过设计避免隐式缩小:

Bool b1(45); // yields warnings, but it compiles
Bool b2{3};  // not ok by standard

我尝试使用模板伤害自己,但没有成功。

我怎样才能让它工作?

【问题讨论】:

  • 您可能还想查看一些有关安全布尔的现有技术。例如,this page 经历了制作安全布尔值的过程,并解释了在此过程中可能遇到的一些陷阱(例如,operator bool 带有一些其他演员可以避免的意外副作用)
  • 谢谢!我一定会看看的!

标签: c++ boolean safe-bool-idiom


【解决方案1】:

您可以通过显式删除所有其他构造函数来实现此目的。

struct Bool final
{
    template<class T>
    Bool(T) = delete;

    Bool(bool value);
};

【讨论】:

  • _value 的声明在哪里?您是否为了简洁而忽略了这一点?
  • 那段代码也会(隐式地)删除默认构造函数,对吧?
  • @gurka 见default constructor。 “如果没有为类类型提供任何类型的用户声明的构造函数[...],编译器将始终将默认构造函数声明为其类的内联公共成员。”。只需提供Bool(bool value) 就足以删除默认构造函数。
  • 如此简单,如此优雅。
  • @AdvSphere 正如您所指出的,explicit 不会阻止Bool foo{true};,也不会阻止Bool foo{1};。第二种情况是 OP 试图阻止的。通过删除构造函数模板,您可以防止Bool foo{1};,因为它会绑定到已删除的构造函数。
【解决方案2】:

添加和显式删除模板构造函数:

template <typename T>
Bool(T) = delete;

它比其他构造函数更好地匹配实际 bool 以外的任何内容,从而防止隐式转换。

【讨论】:

  • “除了实际的 bool 之外的任何东西”——和 Bool:这不会,也可能不应该禁用编译器生成的复制和移动构造函数。
【解决方案3】:

如果您只需要:
一个只有“真”或“假”且不能隐式转换为 int/char/pointer 的变量,然后我会考虑使用枚举类:

enum class Bool {
    False,
    True,
};

【讨论】:

    【解决方案4】:

    我正在尝试设计一个应用安全 bool 习惯用法的 bool 包装结构。

    不要。

    安全的 bool 习惯用法仅在 C++03 及更早版本中相关 - 如果您通过执行以下操作来表达您的类型是“真实的”:

    struct A {
        operator bool() const;
    };
    

    你会遇到各种各样的问题,比如:

    A{} + 4;    // ok?!
    A{} < 0;    // ok?!
    A{} == B{}; // ok if B also has operator bool??!
    

    所以安全布尔成语是解决这个意外隐式转换问题的方法,使用函数指针(当然,函数指针!)。

    在 C++11 中,我们有一个更好的方法

    struct A {
        explicit operator bool() const;
    };
    

    正是我们想要的。其实是literally designed解决了这个问题。虽然安全的布尔成语是相当复杂的脚手架,但explicit operator bool 使用起来超级简单,而且只是做正确的事。您不需要包装器 - 实际上使用包装器比直接编写 explicit operator bool 更难。

    此外,您的包装器对用户施加了 (a) 不可派生性,因为您将 Bool 设为 final 和 (b) 额外的 bool 成员,您必须保持同步,因此它引入而不是解决问题.考虑一下您需要执行多少工作:

    template <class T>
    struct my_unique_ptr : Bool { ... };
    

    template <class T>
    struct my_unique_ptr {
        T* ptr;
    
        explicit operator bool() const { return ptr; }
    };
    

    【讨论】:

      猜你喜欢
      • 2017-07-31
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2023-03-25
      • 1970-01-01
      • 2020-05-08
      • 2018-04-23
      • 2011-02-06
      相关资源
      最近更新 更多