【问题标题】:Limitations of the conditional operator ?:条件运算符 ? 的限制:
【发布时间】:2011-09-05 02:55:02
【问题描述】:

我使用的是 GCC 4.5,并且观察到了非常奇特的行为。我想知道这个运算符是否有我不完全理解的东西。我以为我精通C++。 我有一个瘦 C++ 包装类 Wnd 用于 Windows HWND 对象,并实现了强制转换运算符 operator HWND ...

如果我使用这样的条件运算符(给定输入 Wnd *p 和示例函数 SetParent(HWND))

SetParent((p!=NULL) ? (HWND)(*p) : NULL)

父级正确设置为NULLp,具体取决于。这是我所期望的。 不过如果敢偷懒写:

SetParent(p ? *p : NULL)

事情变得一团糟。 运行 GDB 后,我发现在调用 SetParent 之后,在变量 p 上调用了析构函数。 有什么想法吗?

编辑 这是我的 Wnd 课程:

class Wnd{
        HWND m_hwnd;        ///< the actual handle
        WndFake *fake;      ///< store state here if we do not have a handle
    public:
        virtual ~Wnd();
        //contructor s
        Wnd(HWND wnd=NULL):m_hwnd(wnd),fake(NULL){}
        Wnd(DWORD sty,const jchar *title,const RECT &sz);
        operator HWND(){return m_hwnd;}
        operator HWND() const {return m_hwnd;}
    }

【问题讨论】:

  • 您可能需要显示完整的Wnd 类定义。
  • @bacchus,请注意您的(HWND)) 编辑错误:D 虽然改进后的间距很好......
  • @sarnold 额外的括号来自句子。我错过了。谢谢你的警告;)
  • 请提供Wnd的定义。没有它,这个问题就完全无法回答。
  • -1。这是一个不好的问题。提问时应该学会发布相关代码。

标签: c++ conditional destructor operator-keyword conditional-operator


【解决方案1】:

?: 运算符的操作数必须带入公共类型,将在结果中使用。当你使用

SetParent(p != NULL ? (HWND) *p : NULL);

当编译器选择公共类型为HWND 时,您基本上手动强制这种情况。 IE。上面的变体等价于

SetParent(p != NULL ? (HWND) *p : (HWND) NULL);

但是当你这样做时

SetParent(p != NULL ? *p : NULL);

语言的规则不同,编译器会以不同的方式决定通用类型。在这个常见的类型不是HWND,而是你的Wnd。两个操作数都转换为Wnd,后一个变体被解释为

SetParent(p != NULL ? *p : Wnd(NULL));

即当p 为空时,编译器构造一个临时对象Wnd(NULL)(使用您提供的转换构造函数)并将其作为结果返回。此外,编译器很可能也会在真正的分支中构造一个临时对象(通过使用复制构造函数)。然后将生成的临时对象转换为HWND 类型(因为这是SetParent 要求的),所以整个事情被解释为

SetParent((HWND) (p != NULL ? Wnd(*p) : Wnd(NULL)));

临时对象在调用SetParent 后立即被销毁。这是您观察到的破坏,只是您错误地将其解释为 p 的破坏。

编译器之所以可以选择这种方法是因为你的转换构造函数没有声明explicit。如果声明转换构造函数explicit

class Wnd {
  ...
  explicit Wnd(HWND wnd=NULL) : m_hwnd(wnd), fake(NULL) {}
  ...
};

编译器将无法再将NULL 隐式转换为Wnd。在这种情况下,编译器将别无选择,只能使用 HWND 作为通用类型

SetParent(p != NULL ? (HWND) *p : (HWND) NULL);

如你所愿。

附:当您的班级中已经有 operator HWND() const 时,实现相同的非常量版本 operator HWND() 是没有意义的。

【讨论】:

    【解决方案2】:

    我怀疑你的 Wnd 也有非显式转换构造函数,它需要 HWND 甚至 int? 如果是这样,请明确说明。

    您的 Wnd 可能没有声明复制构造函数和 operator=?声明它们是私有的,不要定义它们。

    同时删除operator HWND 并将成员函数HWND hwnd() const; 添加到您的Wnd。然后代码看起来像这样可读:

    Setparent( p ? p->hwnd() : NULL );
    

    我相信当这些模组完成后,你会发现你的 Wnd 出了什么问题。

    问题很明显,因为 : in ?: 两边的操作数必须是相同的类型,所以 NULL (0) 可以以某种方式转换为 Wnd。所以 *p 的临时副本被制作为 ?: 的返回值,然后调用操作符 HWND。

    【讨论】:

    • +1 我认为删除从HWNDWnd 的隐式转换应该可以解决问题,因为这会禁用从NULLWnd 的转换,并且编译器将被强制执行向相反的方向转换。无论如何,您完全正确,删除隐式转换将使代码更具可读性和可维护性。
    【解决方案3】:

    是在变量 p 上调用了析构函数,还是在某个临时变量上调用了 p 的副本?

    在您的第一个示例中,您使用 c 样式转换将 *p 转换为 HWND。第二,您让编译器进行转换,这很可能涉及复制*p

    【讨论】:

      猜你喜欢
      • 2011-01-12
      • 2017-12-03
      • 2013-03-22
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2020-08-22
      • 1970-01-01
      相关资源
      最近更新 更多