代码之家  ›  专栏  ›  技术社区  ›  Mike Lewis

可以用重载的bool()计算对布尔值的构造函数调用吗?

c++
  •  4
  • Mike Lewis  · 技术社区  · 15 年前

    如果bool()运算符被重载,构造函数调用是否可以计算为布尔值?

    class A
    {
      public:
      A() {};
      operator bool() const { return true; }
    }
    
    main()
    {
      if (A a = A())
      {
      // do stuff
      }
    }
    

    上述代码是否有效,或者我是否需要实现以下主要代码:

    int main(int argc, const char* argv[])
    {
    A a();
    if (a)
      {
      // do stuff
      }
    }
    

    这段代码将在我的代码库中随处可见,因此减少行数、增加易读性和缩小范围是很重要的,并且会因此得到改进。

    有什么想法吗?

    4 回复  |  直到 15 年前
        1
  •  8
  •   Johannes Schaub - litb    15 年前

    代码包含一些语法和语义错误。让我们修复它们

    class A
    {
    public:
      A() {};
      operator bool() { return true; }
    };
    
    int main()
    {
      if (A a = A())
      {
        // do stuff
      }
    }
    

    您可以选择将转换函数中的类型更改为其他类型。如前所述,布尔转换也将成功地转换为任何整数类型。转换为 void* 将转换限制为bool和 空洞* 这是一个常用的成语。另一个更好的方法是转换为某种私有类型,称为 safe bool idiom .

    class A
    {
    private:
      struct safe_bool { int true_; };
      typedef int safe_bool::*safe_type;
    public:
      A() {};
      operator safe_type() { return &safe_bool::true_; }
    };
    

    回到语法:如果您有其他部分,可以使用声明变量的名称,因为它仍然在作用域中。在所有分支处理成功后销毁

    if(A a = A())
    { ... }
    else if(B b = a)
    { ... }
    

    您也可以像以前一样使用相同的名称,并且变量将隐藏其他变量,但是您不能在任何分支的最外层块中声明相同的名称-它将与其他声明冲突,而不是隐藏。

    if(int test = 0)
    { ... }
    else
    { int test = 1; /* error! */ }
    

    声明和初始化变量的技术通常与 dynamic_cast 不过,也可以与上面这样的用户定义类型完美结合使用

    if(Derived *derived = dynamic_cast<Derived*>(base)) {
      // do stuff
    }
    

    注意,在语法上,必须初始化变量(使用 = expression 形式类似于默认参数)。以下内容无效

    if(ifstream ifs("file.txt")) {
      // invalid. Syntactic error
    }
    
        2
  •  2
  •   Steve Jessop    15 年前

    第一个问题的答案是“是”,但您的“构造函数调用”是错误的。 A a 声明变量。这是一个陈述,而不是一个表达。 A() 是构造以下对象的匿名临时实例的表达式:

    struct A
    {
        A() {};
        operator bool() { return true; }
    };
    
    int main()
    {
        if (A())
        {
            // do stuff
        }
    }
    

    如果要在“stufacture”中使用a的实例,则需要:

    if (A a = A()) {
        // do stuff
    }
    
        3
  •  2
  •   Pavel Minaev    15 年前

    您可以这样做,但仅当您使用复制初始化语法调用构造函数时。例如:

    main()
    {
        if (A a = A())
        {
           // do stuff
        }
    }
    

    当启用优化时,大多数编译器都会在这样的初始值设定项中省略复制构造函数,但是仍然需要一个可访问的构造函数。

    当然,如果 A 会有一个构造函数,例如 A(int) 您也可以这样做:

    if (A a = 123) ...
    

    此外,通常认为 operator bool() 为了这样的目的。原因是 bool 然后隐式转换为任何数字类型 false == 0 && true == 1 );例如,类的客户机可以编写:

    int x = A();
    
    void foo(float y);
    foo(A());
    

    这可能不是你想要允许的。一个简单的技巧是使用指向私有类成员的指针:

    class A {
    private:
        struct dummy_t { int dummy; };
        typedef int dummy_t::*unspecified_boolean_type;
    public:
        operator unspecified_boolean_type() const {
            if (...) {
                return &dummy_t::dummy; // true
            } else {
                return 0; // false
            }
        }
    };
    

    指向成员的指针具有隐式 布尔 转换(和往常一样,空指针是 false ,任何其他条件都是正确的),但它们与除自己的类型之外的任何类型都不兼容;而且,由于这里的内部类是私有的,因此任何客户端代码都不可能声明该类型的变量。( auto decltype 在C++ 0x中提供了一种方法。

    作为旁注, main() 由于写不有效,C++ + ISO C++没有“默认” int “按C的方式规则,没有显式返回类型的函数无效。

        4
  •  0
  •   GManNickG    15 年前

    如果你想指出失败,为什么不呢? throw an exception ?

    #include <stdexcept>
    
    class Foo
    {
    public:
        Foo(void)
        {
            if (/* something bad D: */)
            {
                throw std::runtime_error("Couldn't open file, etc...");
            }
        }
    }
    
    int main(void)
    {
        try
        {
            Foo f;
            // do stuff with f
        }
        catch (std::exception& e)
        {
            std::cerr << "Error: " << e.what() << std::endl;
        }
    }