条件运算符的局限性?:

时间:2011-05-31 00:05:18

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

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

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

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

父母已正确设置为NULLp。这就是我所期待的。 但是,如果敢于懒惰并写下:

SetParent(p ? *p : NULL)
事情变得混乱。 运行GDB后,我发现在调用p之后,变量SetParent上会调用析构函数。 任何想法在这里发生了什么?

修改 这是我的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;}
    }

3 个答案:

答案 0 :(得分:4)

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

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

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

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

我相信,当这些mod完成后,你会发现你的Wnd有什么问题。

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

答案 1 :(得分:3)

析构函数是在变量p上调用还是在某个临时变量上调用p?

的副本

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

答案 2 :(得分:2)

必须将?:运算符的操作数设置为通用类型,这将在结果中使用。当你使用

SetParent(p != NULL ? (HWND) *p : NULL);
当编译器选择公共类型为HWND时,你基本上手动强制执行这种情况。即以上变体等同于

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

但是当你做的时候

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

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

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

即。当p为null时,编译器构造一个临时对象Wnd(NULL)(使用您提供的转换构造函数)并将其作为结果返回。而且,编译器很可能也会在true分支中构造一个临时对象(通过使用复制构造函数)。然后,生成的临时对象将转换为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) {}
  ...
};

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

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

就像你想要的那样。

P.S。当您的班级中已经有operator HWND() const时,实施相同的非常量版本operator HWND()毫无意义。