【问题标题】:Can you evaluate a constructor call to boolean with an overloaded bool()?您可以使用重载的 bool() 评估对 boolean 的构造函数调用吗?
【发布时间】:2010-11-25 16:38:16
【问题描述】:

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

class A
{
  public:
  A() {};
  operator bool() const { return true; }
}

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

上面的代码是否有效,还是我需要像这样实现main:

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

这段代码将在我的代码库中到处出现,因此更少的行数、更高的易读性和更小的范围很重要,并且会因此得到改进。

有什么想法吗?

【问题讨论】:

  • 你对这段代码的意图是什么?转换为 bool 总是会返回 true 吗?如果是这样,if 语句是多余的。
  • 我怀疑这是一个快速示例代码。 “bool operator() () {return foo;}”不是更好的例子吗?
  • 这是干什么用的?你在检查错误吗?
  • 是的,这只是表达想法的草率代码。 if() 中的内容是我真正想要的。
  • 它应该是operator bool() const {...} - 没有返回类型,并且不修改*this 所以const。

标签: c++


【解决方案1】:

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

struct A
{
    A() {};
    operator bool() { return true; }
};

int main()
{
    if (A())
    {
        // do stuff
    }
}

如果你想在“stuff”中使用A的实例,那么你需要:

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

【讨论】:

  • 其实第二个问题的答案是“是”:下面是完全合法的C++:if (A a = A()) { /* use a */ }
【解决方案2】:

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

class A
{
public:
  A() {};
  operator bool() { return true; }
};

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

您可以选择将转换函数中的类型更改为其他类型。如所写,布尔转换也将成功转换为任何整数类型。转换为void* 将限制转换为只有bool 和void*,这是一个常用的习惯用法。另一种更好的方法是转换为一些私有类型,称为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_; }
};

回到语法:如果你有一个 else 部分,你可以使用声明变量的名称,因为它仍在范围内。所有分支处理成功后销毁

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
}

【讨论】:

  • 不仅仅是“初始化”——即使存在匹配的构造函数,if (A a(123)) 之类的东西也会无效。就像for一样,它需要有=的复制初始化表单。
  • 是的,没错。我会澄清 :) 就是说,像for(A a(123); ; ); 这样的东西是完全有效的,除非你的意思是for(; A a(123); ); :)
  • 嘿,我没有意识到for 实际上让你写这个,但你就在那里。另外,+1 提到当涉及dynamic_cast 时这是最方便的(我觉得奇怪的是这么多 C++ 开发人员似乎不知道这个成语)。
  • litb - 感谢您的澄清。没有意识到需要变量初始化。
  • 作为参考,另请参阅:安全布尔成语在 C++11 中是否已过时? stackoverflow.com/questions/6242768/…
【解决方案3】:

您可以这样做,但前提是您使用复制初始化语法来调用您的构造函数。例如:

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

当启用优化时,大多数编译器会在此类初始化程序中省略复制构造函数,但仍然需要可访问的构造函数。

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

if (A a = 123) ...

此外,出于此类目的,在课堂上使用operator bool() 通常被认为是一个坏主意。原因是bool 又可以隐式转换为任何数字类型(使用false == 0 &amp;&amp; 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
        }
    }
};

指向成员的指针有一个隐式的bool 转换(像往常一样,空指针是false,其他都是真的),但它们不兼容除它们自己的任何类型;并且,由于这里的内部类是私有的,因此没有客户端代码可以声明该类型的变量(不过,C++0x 中的autodecltype 提供了一种方法)。

附带说明,所写的main() 是无效的 C++ - ISO C++ 不像 C 那样具有“默认 int”规则,并且没有显式返回类型的函数是无效的。

【讨论】:

  • "main() aswritten is not valid C++" - 并且类定义需要一个尾随分号,并且 operator bool() 没有返回类型,并且它(如构造函数A) 是私有的。
  • 呃,当我在改进我的答案时,你也想出了 safe-bool。哈哈 +1 为一个很好的答案 tho xD
  • 最近的编辑是错误的。将 const 引用绑定到右值仍然需要复制构造函数。实现可能决定省略副本,但复制构造函数必须仍然是可调用的。请参阅以下 DR:open-std.org/jtc1/sc22/wg21/docs/cwg_defects.html#391open-std.org/jtc1/sc22/wg21/docs/cwg_defects.html#291(这只是澄清措辞 - 这不是要求复制 ctor 等)。如果进行测试,请务必禁用 C++0x 模式,因为在 C++0x 中,不会调用或不需要复制构造函数。
  • 好点;我在 VC10 上检查了它,它似乎在其他 C++0x 扩展中也有这个。我会编辑这个。
【解决方案4】:

如果您试图指示失败,为什么不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;
    }
}

【讨论】:

  • 在我开发的可爱的嵌入式环境中,例外并不是我们拥有的奢侈品。模板是我们的编译器允许我们做的最疯狂的事情。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2010-11-02
  • 1970-01-01
  • 1970-01-01
  • 2021-07-09
相关资源
最近更新 更多