C++ 条件运算符的限制?:

C++ 条件运算符的限制?:,c++,conditional,destructor,operator-keyword,conditional-operator,C++,Conditional,Destructor,Operator Keyword,Conditional Operator,我正在使用GCC4.5,并且观察到了非常奇怪的行为。我想知道这个操作员是否有什么我不完全理解的地方。我认为我精通C++。 我有一个瘦的C++包装类 WND > Windows >代码> HWND < /Cord>对象,其中有一个已实现的CAST运算符操作符HWND…< /Cord> 如果我像这样使用条件运算符(给定输入Wnd*p和示例函数SetParent(HWND)): 父项正确设置为NULL或p,具体取决于。这就是我所期望的。 但是如果你敢偷懒,写: SetParent(p ? *p :

我正在使用GCC4.5,并且观察到了非常奇怪的行为。我想知道这个操作员是否有什么我不完全理解的地方。我认为我精通C++。 我有一个瘦的C++包装类<代码> WND <代码> > Windows >代码> HWND < /Cord>对象,其中有一个已实现的CAST运算符<代码>操作符HWND…< /Cord> 如果我像这样使用条件运算符(给定输入
Wnd*p
和示例函数
SetParent(HWND))

父项正确设置为
NULL
p
,具体取决于。这就是我所期望的。 但是如果你敢偷懒,写:

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;}
    }
class-Wnd{
HWND m_HWND;//<实际句柄
WndFake*fake;//<如果我们没有句柄,请在此处存储状态
公众:
虚拟~Wnd();
//承包商
Wnd(HWND-Wnd=NULL):m_-HWND(Wnd),fake(NULL){}
Wnd(DWORD sty、const jchar*title、const RECT&sz);
运算符HWND(){return m_HWND;}
运算符HWND()常量{return m_HWND;}
}

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


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

的副本。我怀疑您的Wnd也有非显式转换构造函数,它接受HWND甚至int? 如果是这样的话,那么就把它说清楚

您的Wnd可能没有声明复制构造函数和运算符=吗?将这些声明为私有,而不定义它们

同时删除
操作符HWND
并添加成员函数
HWND HWND()const到您的Wnd。然后,代码将看起来可读,如下所示:

Setparent( p ? p->hwnd() : NULL );
我相信当这些修改完成后,你会发现你的Wnd有什么问题


问题的出现是因为:in?:两侧的操作数必须是相同的类型,因此NULL(0)可以转换为Wnd。因此,*p的临时副本作为?:的返回值,然后调用运算符HWND。

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

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));
SetParent((HWND) (p != NULL ? Wnd(*p) : Wnd(NULL)));
也就是说,当
p
为null时,编译器构造一个临时对象
Wnd(null)
(使用您提供的转换构造函数),并将其作为结果返回。此外,编译器也很可能在真分支中构造一个临时对象(通过使用复制构造函数)。然后将生成的临时对象转换为
HWND
类型(因为这是
SetParent
所需要的),因此整个过程被解释为

SetParent(p != NULL ? *p : Wnd(NULL));
SetParent((HWND) (p != NULL ? Wnd(*p) : Wnd(NULL)));
调用
SetParent
后,临时对象立即被销毁。这是您观察到的破坏,只是您错误地将其解释为
p
的破坏

编译器选择这种方法的原因是,转换构造函数没有声明为显式的。如果您声明转换构造函数
explicit

class Wnd {
  ...
  explicit Wnd(HWND wnd=NULL) : m_hwnd(wnd), fake(NULL) {}
  ...
};
编译器将不再能够使用将
NULL
隐式转换为
Wnd
。在这种情况下,编译器将别无选择,只能使用
HWND
作为通用类型

SetParent(p != NULL ? (HWND) *p : (HWND) NULL);
就像你想的那样


另外,当类中已经有了
运算符HWND()const
时,实现相同的非const版本
运算符HWND()

没有意义,您可能需要显示完整的
Wnd
类定义。@bacchus,注意您的
(HWND))
edit是错误的:D虽然改进的间距很好…@sarnold额外的括号来自句子。我错过了。谢谢你的警告;)请提供
Wnd
的定义。没有它,这个问题是完全无法回答的。这是个糟糕的问题。一个人应该学会在提问时发布相关代码。+1我认为删除从
HWND
Wnd
的隐式转换应该可以做到这一点,因为这会禁用从
NULL
Wnd
的转换,编译器将被迫向相反方向转换。无论如何,您完全正确地认为删除隐式转换将使代码更具可读性和可维护性。