Есть у меня маленький класс Handle, реализующий RAII для виндового типа HANDLE (общий тип для объектов ядра). Сам тип HANDLE определен в винде, как void *. В моем классе, как положено, есть конструктор с параметром типа HANDLE, и метод operator HANDLE.
Все было прекрасно, пока я однажды не написал:
HANDLE Sync = (Timer != 0)? Timer : Event;
где Timer — объект класса Handle, а Event — обычная переменная типа HANDLE.
Я ожидал, что компилятор преобразует Timer к HANDLE, а он, наоборот, преобразует Event к объекту класса Handle, создавая временный объект, и тут же уничтожая его, что приводит к вызову деструктора и закрытию хэндла.
Такая же хрень происходит, если вместо HANDLE использовать int или другой интегральный тип.
Можно ли как-то указать в классе, чтобы в подобных случаях объект преобразовывался к простому типу через соответствующий метод, а не наоборот?
Здравствуйте, Евгений Музыченко, Вы писали:
ЕМ>HANDLE Sync = (Timer != 0)? Timer : Event;
ЕМ>где Timer — объект класса Handle, а Event — обычная переменная типа HANDLE.
ЕМ>Я ожидал, что компилятор преобразует Timer к HANDLE, а он, наоборот, преобразует Event к объекту класса Handle, создавая временный объект,
Если объявить конструктор вашего класса Handle как explicit, то автоматических преобразований не будет
Re: Управление преобразованием типов в условной операции
Здравствуйте, Евгений Музыченко, Вы писали:
ЕМ>Можно ли как-то указать в классе, чтобы в подобных случаях объект преобразовывался к простому типу через соответствующий метод, а не наоборот?
Как уже подсказали, у тебя проблема в дизайне обертки и архитектуры класса в целом. Класс обертка Handle (вызывающий закрытие хендла в деструкторе) — владеющий, а просто объект HANDLE — нет. Очень опасно делать класс обертку с конструктором который неявно может принимать HANDLE. Сделай дизайн похожим на unique_ptr, если хендл у тебя не шарится вовсе, типа такого:
Во дела!
ЕМ>Можно ли как-то указать в классе, чтобы в подобных случаях объект преобразовывался к простому типу через соответствующий метод, а не наоборот?
Здравствуйте, Евгений Музыченко, Вы писали:
ЕМ>Я ожидал, что компилятор преобразует Timer к HANDLE, а он, наоборот, преобразует Event к объекту класса Handle, создавая временный объект, и тут же уничтожая его, что приводит к вызову деструктора и закрытию хэндла.
ЕМ>Такая же хрень происходит, если вместо HANDLE использовать int или другой интегральный тип.
ЕМ>Можно ли как-то указать в классе, чтобы в подобных случаях объект преобразовывался к простому типу через соответствующий метод, а не наоборот?
Здравствуйте, Zhendos, Вы писали:
Z>Если объявить конструктор вашего класса Handle как explicit, то автоматических преобразований не будет
Спасибо, помогло. Теперь вспомнил, что в разное время несколько раз читал про explicit, но пользоваться не приходилось, и в нужный момент не вспомнилось.
Re[2]: Управление преобразованием типов в условной операции
Здравствуйте, _NN_, Вы писали:
_NN>Может возьмёте готовое решение чем писать который раз тот же код ?
Не люблю добавлять зависимости без крайней нужды. Если б какой сложный алгоритм, то имело бы смысл подключить библиотеку, а так я и этот Handle сделал исключительно для вящей красоты.
Re[3]: Управление преобразованием типов в условной операции
Z>>Если объявить конструктор вашего класса Handle как explicit, то автоматических преобразований не будет
ЕМ>Спасибо, помогло. Теперь вспомнил, что в разное время несколько раз читал про explicit, но пользоваться не приходилось, и в нужный момент не вспомнилось.
Да проще его тогда вообще не объявлять
А присвоения через void operator = (HANDLE q); сделать
Re[4]: Управление преобразованием типов в условной операции
Здравствуйте, Евгений Музыченко, Вы писали:
ЕМ>Есть у меня маленький класс Handle, реализующий RAII для виндового типа HANDLE (общий тип для объектов ядра).
А я для этой цели пользуюсь напрямую std::unique_ptr без оберток:
using handle_holder_t = std::unique_ptr<std::remove_pointer<HANDLE>, decltype([](HANDLE h) { ::CloseHandle(h); })>;
Лябмды без состояния конструируемы по умолчанию.
Преимущества:
* не присваивать постоянно указатель на CloseHandle.
* размер: подлежит оптимизации пустого функтора через базу или [no_unique_address]], если unique_ptr её реализует
Русский военный корабль идёт ко дну!
Re[3]: Управление преобразованием типов в условной операции
Здравствуйте, Alexander G, Вы писали:
AG>Лябмды без состояния конструируемы по умолчанию. AG>Преимущества: AG>* не присваивать постоянно указатель на CloseHandle. AG>* размер: подлежит оптимизации пустого функтора через базу или [no_unique_address]], если unique_ptr её реализует
Все классно, только не понятно зачем тут С++20, кроме как для краткости ?!
Здравствуйте, Alexander G, Вы писали:
AG>Сам указатель на CloseHandle надо ещё передавать отдельно при конструировании/присвоении.
А, понятно. Я никогда не пользовался unique_ptr и подобными шаблонами из std — не люблю этого многокаскадного шаманства. Но вместо него можно сделать простенький универсальный шаблонный класс с параметрами типа значения и указателя на функцию, которая вызывается с этим значением. Оптимизатор VC++ даже достаточно умен, чтобы вызывать ее статически, если указатель передан непосредственно.