Ограничения условного оператора?: - PullRequest
6 голосов
/ 31 мая 2011

Я использую GCC 4.5 и наблюдаю очень своеобразное поведение.Мне интересно, есть ли что-то с этим оператором, что я не до конца понимаю.Я думал, что я был опытным в C ++.У меня есть тонкий класс оболочки C ++ Wnd для Windows HWND объектов с реализованным оператором приведения operator HWND ....

Если я использую условный оператор, подобный этому (данные ввода Wnd *p и пример функции SetParent(HWND)):

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

Родитель правильно настроен на NULL или p, в зависимости от того, чего я ожидал. Однако, если осмелитесь быть ленивым и напишите:

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 ]

4 голосов
/ 31 мая 2011

Я подозреваю, что у вашего Wnd также есть неявный конструктор преобразования, который принимает HWND или даже int? Если так, то сделайте это явным.

Ваш Wnd, вероятно, не имеет конструктора копирования и оператора = объявлен? объявляйте их частными и не определяйте их.

Также удалите operator HWND и добавьте функцию-член HWND hwnd() const; в свой Wnd. Тогда код будет выглядеть так:

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

Я верю, что когда эти моды будут сделаны, вы узнаете, что не так с вашим Wnd.

Проблема проявляется потому, что операнды с обеих сторон: in?: Должны быть одного типа, поэтому NULL (0) может быть каким-то образом преобразован в Wnd. Таким образом, временная копия * p создается как возвращаемое значение?: Тогда к ней вызывается оператор HWND.

3 голосов
/ 31 мая 2011

Деструктор вызывается для переменной p или для некоторой временной переменной, которая является копией p?

В первом примере вы используете приведение в стиле c для преобразования *p в HWND.Во втором случае вы позволяете компилятору выполнять преобразование, и это может включать создание копии *p.

2 голосов
/ 06 июня 2011

Операнды оператора ?: должны быть приведены к общему типу, который будет использоваться в результате.Когда вы используете

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 равен нулю, компилятор создает временный объект 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);

, как вы и хотели.

PS Когда у вас уже есть operator HWND() const в вашем классе нет никакого смысла в реализации идентичной неконстантной версии operator HWND().

...