Есть некая иерархия, элементы которой имеют интрузивное управление временем жизни (а-ля COM).
Сейчас это просто AddRef/Release. Ну, для полноты картины, есть еще и QueryInterface.
Поступило предложение сделать эти методы константными. Основной аргумент — с точки зрения пользователя эти методы не меняют состояние объекта.
Интересует мнение общественности — насколько допустима такая трактовка? Вообще, имеет ли право на жизнь const в абстрактных интерфейсах?
Аргументы за и против?
Например, в большинстве распространненных фреймворках такие методы неконстантны (точнее, я не знаю ни одного, где они или их аналоги константны).
AS>Есть некая иерархия, элементы которой имеют интрузивное управление временем жизни (а-ля COM). AS>Сейчас это просто AddRef/Release. Ну, для полноты картины, есть еще и QueryInterface.
AS>Поступило предложение сделать эти методы константными. Основной аргумент — с точки зрения пользователя эти методы не меняют состояние объекта.
Мне что-то кажется, что последний Release может очень капитально поменять состояние объекта.
AS>Например, в большинстве распространненных фреймворках такие методы неконстантны (точнее, я не знаю ни одного, где они или их аналоги константны).
AS>Поступило предложение сделать эти методы константными. Основной аргумент — с точки зрения пользователя эти методы не меняют состояние объекта.
Рассуждение от противного — с точки зрения пользователя вызов метода происходит либо для изменения состояния объекта (const недопустим), либо для опроса состояния объекта (const допустим). В данном случае ничего не опрашиваем, а следовательно — ...
Как много веселых ребят, и все делают велосипед...
Здравствуйте, Andrew S, Вы писали:
AS>Поступило предложение сделать эти методы константными. Основной аргумент — с точки зрения пользователя эти методы не меняют состояние объекта. AS>Интересует мнение общественности — насколько допустима такая трактовка? Вообще, имеет ли право на жизнь const в абстрактных интерфейсах?
Здравствуйте, Andrew S, Вы писали:
AS>Есть некая иерархия, элементы которой имеют интрузивное управление временем жизни (а-ля COM). AS>Сейчас это просто AddRef/Release. Ну, для полноты картины, есть еще и QueryInterface.
AS>Поступило предложение сделать эти методы константными. Основной аргумент — с точки зрения пользователя эти методы не меняют состояние объекта. AS>Интересует мнение общественности — насколько допустима такая трактовка? Вообще, имеет ли право на жизнь const в абстрактных интерфейсах?
AS>Аргументы за и против?
Если вы планируете сколь-нибудь широко работать с константными экземплярами объектов, у которых хотите вызывать только константные методы, вам придется делать вышеперечисленные методы константными. Иначе вы даже нужный интерфейс у константного объекта запросить не сможете. Как следствие, ваши ленивые разработчики (т.е., все поголовно) с константностью объектов просто не будут связываться.
AS>Например, в большинстве распространненных фреймворках такие методы неконстантны (точнее, я не знаю ни одного, где они или их аналоги константны).
А в этих фреймворках вообще константные методы в интерфейсах поддерживаются?
Одним из 33 полных кавалеров ордена "За заслуги перед Отечеством" является Геннадий Хазанов.
Здравствуйте, Andrew S, Вы писали:
AS>Поступило предложение сделать эти методы константными. Основной аргумент — с точки зрения пользователя эти методы не меняют состояние объекта. AS>Интересует мнение общественности — насколько допустима такая трактовка? Вообще, имеет ли право на жизнь const в абстрактных интерфейсах?
Допустима, ценой небольших (? зависит от того, сколько уже напедалили) затрат на гигиену кода, во-первых, и потерей совместимости с настоящим COM, во-вторых.
Подсчёт ссылок — элементарно, у реализации нужно объявить mutable счётчик.
А QueryInterface придётся обернуть: вызов этой функции над константным/неконстантным умным указателем ведёт к вызову метода (константного) у указуемого объекта и наложению/снятию константности на результат.
Здравствуйте, ononim, Вы писали:
O>Рассуждение от противного — с точки зрения пользователя вызов метода происходит либо для изменения состояния объекта (const недопустим), либо для опроса состояния объекта (const допустим). В данном случае ничего не опрашиваем, а следовательно — ...
Все эмоциональные формулировки не соотвествуют действительному положению вещей и приведены мной исключительно "ради красного словца". За корректными формулировками и неискажённым изложением идей, следует обращаться к их автором или воспользоваться поиском
O>>Рассуждение от противного — с точки зрения пользователя вызов метода происходит либо для изменения состояния объекта (const недопустим), либо для опроса состояния объекта (const допустим). В данном случае ничего не опрашиваем, а следовательно — ...
E>Моё мнение
Здравствуйте, Erop, Вы писали:
E>Здравствуйте, ononim, Вы писали:
O>>Рассуждение от противного — с точки зрения пользователя вызов метода происходит либо для изменения состояния объекта (const недопустим), либо для опроса состояния объекта (const допустим). В данном случае ничего не опрашиваем, а следовательно — ...
E>Моё мнение
Хорошее мнение . Хочу подкрепить фактами насчёт "нарваться при снятии истинной константности". Действительно нарывался при программировании под Palm с использованием CodeWarrior`а: снятие истинной константности с применением C-style каста ((int)val) приводило к системным ошибкам доступа. На тот момент времени разбираться не было, но по симптомам было похоже, что компилятор с целью оптимизации размещал константы в том сегменте, который не загружался в область памяти, доступную для изменения (она на Palm`ах была весьма дорогой). Правда применение C++ стиля для снятия константности (const_cast<int>(val)) решало проблему. Так что вы правы, но отчасти: const_cast для снятия истинной константности применять можно, а вот C-style cast — это аналог reinterpret_cast`а — его применять для этих целей нельзя ни в коем случае...
Компьютер сделает всё, что вы ему скажете, но это может сильно отличаться от того, что вы имели в виду.
Здравствуйте, Mr. None, Вы писали:
O>>>Рассуждение от противного — с точки зрения пользователя вызов метода происходит либо для изменения состояния объекта (const недопустим), либо для опроса состояния объекта (const допустим). В данном случае ничего не опрашиваем, а следовательно — ...
E>>Моё мнение
, во всяком случае пока, другое...
MN>Хорошее мнение . Хочу подкрепить фактами насчёт "нарваться при снятии истинной константности". Действительно нарывался при программировании под Palm с использованием CodeWarrior`а: снятие истинной константности с применением C-style каста ((int)val) приводило к системным ошибкам доступа. На тот момент времени разбираться не было, но по симптомам было похоже, что компилятор с целью оптимизации размещал константы в том сегменте, который не загружался в область памяти, доступную для изменения (она на Palm`ах была весьма дорогой). Правда применение C++ стиля для снятия константности (const_cast<int>(val)) решало проблему.
Ссылка тут не пропущена случаем? Потому что без ссылки или указателя и говорить-то не о чем...
MN> Так что вы правы, но отчасти: const_cast для снятия истинной константности применять можно, а вот C-style cast — это аналог reinterpret_cast`а — его применять для этих целей нельзя ни в коем случае...
Ничего подобного, то, что у вас const_cast не приводил к ошибкам — всего лишь один из вариантов UB Стандарт гляньте, там довольно ясно написано, что снимать константность можно только с алиаса к значению, а не с самого значения.
Одним из 33 полных кавалеров ордена "За заслуги перед Отечеством" является Геннадий Хазанов.
Здравствуйте, Mr. None, Вы писали:
MN>Правда применение C++ стиля для снятия константности (const_cast<int>(val)) решало проблему. Так что вы правы, но отчасти: const_cast для снятия истинной константности применять можно, а вот C-style cast — это аналог reinterpret_cast`а — его применять для этих целей нельзя ни в коем случае...
Ну это от платформы/реализации зависит. Снятие истинной константности, вернее попытка изменить истинную константу -- это неспецифицированное или неопределённое поведение (не помню точно какое из двух) Думаю, что неопределённое.
Все эмоциональные формулировки не соотвествуют действительному положению вещей и приведены мной исключительно "ради красного словца". За корректными формулировками и неискажённым изложением идей, следует обращаться к их автором или воспользоваться поиском
Здравствуйте, Тот кто сидит в пруду, Вы писали:
ТКС>Здравствуйте, Mr. None, Вы писали:
MN>> Так что вы правы, но отчасти: const_cast для снятия истинной константности применять можно, а вот C-style cast — это аналог reinterpret_cast`а — его применять для этих целей нельзя ни в коем случае...
ТКС>Ничего подобного, то, что у вас const_cast не приводил к ошибкам — всего лишь один из вариантов UB Стандарт гляньте, там довольно ясно написано, что снимать константность можно только с алиаса к значению, а не с самого значения.
const int val = 0;
...
(int)val = 2; // вот тут на некоторых видаж Palm имеем системное исключение - попытка записи в недопустимую область памяти
const int val = 0;
...
const_cast<int>(val) = 2; // всё нормально
Компьютер сделает всё, что вы ему скажете, но это может сильно отличаться от того, что вы имели в виду.
Здравствуйте, Erop, Вы писали:
E>Здравствуйте, Mr. None, Вы писали:
MN>>Правда применение C++ стиля для снятия константности (const_cast<int>(val)) решало проблему. Так что вы правы, но отчасти: const_cast для снятия истинной константности применять можно, а вот C-style cast — это аналог reinterpret_cast`а — его применять для этих целей нельзя ни в коем случае...
E>Ну это от платформы/реализации зависит. Снятие истинной константности, вернее попытка изменить истинную константу -- это неспецифицированное или неопределённое поведение (не помню точно какое из двух) Думаю, что неопределённое.
MN>const int val = 0;
MN>...
MN>const_cast<int>(val) = 2; // всё нормально
MN>
В таком варианте VC даже не компилирует:
1>.\testInst.cpp(80) : error C2440: 'const_cast' : cannot convert from 'const int' to 'int'
1> Conversion is a valid standard conversion, which can be performed implicitly or by use of static_cast, C-style cast or function-style cast
Если добавить ссылку — написать
const_cast<int&>(val) = 2;
, то это будет всего лишь разновидность UB. Никто не гарантирует, что всё нормально будет на всех платформах. На пальме может и сработает, а где-нибудь да грохнется.
Одним из 33 полных кавалеров ордена "За заслуги перед Отечеством" является Геннадий Хазанов.
Здравствуйте, Andrew S, Вы писали:
AS>Всем привет.
AS>Есть некая иерархия, элементы которой имеют интрузивное управление временем жизни (а-ля COM). AS>Сейчас это просто AddRef/Release. Ну, для полноты картины, есть еще и QueryInterface.
AS>Поступило предложение сделать эти методы константными. Основной аргумент — с точки зрения пользователя эти методы не меняют состояние объекта. AS>Интересует мнение общественности — насколько допустима такая трактовка? Вообще, имеет ли право на жизнь const в абстрактных интерфейсах?
AS>Аргументы за и против?
AS>Например, в большинстве распространненных фреймворках такие методы неконстантны (точнее, я не знаю ни одного, где они или их аналоги константны).
AS>Спасибо!
* Делал у себя с mutable счётчиком и const'ными addref-release для использования с boost::intrusive_ptr
AS>>Поступило предложение сделать эти методы константными. Основной аргумент — с точки зрения пользователя эти методы не меняют состояние объекта. AS>>Интересует мнение общественности — насколько допустима такая трактовка? Вообще, имеет ли право на жизнь const в абстрактных интерфейсах?
К>Допустима, ценой небольших (? зависит от того, сколько уже напедалили) затрат на гигиену кода, во-первых, и потерей совместимости с настоящим COM, во-вторых.
А что имеется в виду под потерей совместимости? Ведь все равное от кома и обратно нужны адаптеры, так что...
К>Подсчёт ссылок — элементарно, у реализации нужно объявить mutable счётчик.
Реализация понятна Интересуют аргументы за и против наличия вышеуказанных константных методов у интерфейсов. Взять тот же ice — там интрузивные операции неконстантны.
К>А QueryInterface придётся обернуть: вызов этой функции над константным/неконстантным умным указателем ведёт к вызову метода (константного) у указуемого объекта и наложению/снятию константности на результат.
В смысле, писАть const objptr<Interface> ? Или все-таки правильнее objptr<const Interface>, и ничего не оборачивать?
Здравствуйте, Andrew S, Вы писали:
AS>А что имеется в виду под потерей совместимости? Ведь все равное от кома и обратно нужны адаптеры, так что...
Ну, например, Windows Platform SDK поставляется с .h-файлами объявлений интерфейсов. Будешь переделывать?
Также придётся допилить трансляцию .idl, потому что у IUnknown методы неконстантные.
На самом деле, проще подстроиться под ком. Всё равно, если пользуешься умными указателями, то сам напрямую функции IUnknown не вызываешь. Так вот, можно допилить умные указатели, чтобы они внутри снимали константность при вызове AddRef/Release/QueryInterface А все остальные методы — вызываются пользователем прямо у объекта, там константность как объявлена, такая и будет.
AS>Реализация понятна Интересуют аргументы за и против наличия вышеуказанных константных методов у интерфейсов. Взять тот же ice — там интрузивные операции неконстантны.
К>>А QueryInterface придётся обернуть: вызов этой функции над константным/неконстантным умным указателем ведёт к вызову метода (константного) у указуемого объекта и наложению/снятию константности на результат.
AS>В смысле, писАть const objptr<Interface> ? Или все-таки правильнее objptr<const Interface>, и ничего не оборачивать?
В смысле,
objptr<const X> cx;
objptr<const Y>(cx)
// превращается вconst Y* y = 0;
cx->QueryInterfaceConst(__uuidof(Y), (void const**)&y);
return objptr<const Y>(y);
objptr<X> mx;
objptr<Y>(mx)
// соответственно,const Y* y = 0;
mx->QueryInterfaceConst(__uuidof(Y), (void const**)&y);
return objptr<Y>(const_cast<Y*>(y)); // вот он, легальный конст-каст
// аналогично, только в обратную сторону, будет внутреннее устройство
// если функция QueryInterface неконстантная
// смешанные случаи
objptr<const Y>(mx); // константность накладывается, всё законно
objptr<Y>(cx); // константность теряется - должны сделать ошибку компиляции
Ну а писать const objptr<X> против objptr<const X> — это то же самое, как X* const против X const*.
Делать глубокую константность — т.е. const objptr<X> ~ X const* const — в общем случае не стоит, т.к. ломается традиционная семантика.
Хотя иногда глубокая константность может пригодиться — например, для объявления агрегатов и подчинённых объектов.
Тогда лучше завести отдельный класс указателя.
AS>>А что имеется в виду под потерей совместимости? Ведь все равное от кома и обратно нужны адаптеры, так что...
К>Ну, например, Windows Platform SDK поставляется с .h-файлами объявлений интерфейсов. Будешь переделывать? К>Также придётся допилить трансляцию .idl, потому что у IUnknown методы неконстантные.
Не-не, IUnknown у нас нет. У нас собственная компонентная модель
К>На самом деле, проще подстроиться под ком. Всё равно, если пользуешься умными указателями, то сам напрямую функции IUnknown не вызываешь. Так вот, можно допилить умные указатели, чтобы они внутри снимали константность при вызове AddRef/Release/QueryInterface А все остальные методы — вызываются пользователем прямо у объекта, там константность как объявлена, такая и будет.
AS>>Реализация понятна Интересуют аргументы за и против наличия вышеуказанных константных методов у интерфейсов. Взять тот же ice — там интрузивные операции неконстантны.
К>>>А QueryInterface придётся обернуть: вызов этой функции над константным/неконстантным умным указателем ведёт к вызову метода (константного) у указуемого объекта и наложению/снятию константности на результат.
AS>>В смысле, писАть const objptr<Interface> ? Или все-таки правильнее objptr<const Interface>, и ничего не оборачивать?
К>Ну а писать const objptr<X> против objptr<const X> — это то же самое, как X* const против X const*. К>Делать глубокую константность — т.е. const objptr<X> ~ X const* const — в общем случае не стоит, т.к. ломается традиционная семантика.
Все, понял, что имеется в виду. Тут все просто — 2 версии QueryInterface, для const и не const.
Здравствуйте, Andrew S, Вы писали:
AS>Не-не, IUnknown у нас нет. У нас собственная компонентная модель
Тогда и проблем с совместимостью нет — за неимением
AS>Все, понял, что имеется в виду. Тут все просто — 2 версии QueryInterface, для const и не const. AS>Это, кстати, один из минусов наличия const.
На самом деле, версия ровно одна! У самого объекта — метод один, для определённости пусть константный.
А у умного указателя — обёртка над этим методом, снимающая константность, если указуемое неконстантно. Тоже одна, очевидно.
Пошаманив с шаблонами, можно обойтись без дублирования. Ну скажем,
template<class To, class From>
objptr< typename copy_constness<From,To>::type > // копируем квалификатор с типа From на To
qi_cast( objptr<From> from )
{
To const* p = 0;
if( from ) from->QueryInterface(
__uuidof( typename remove_constness<To>::type ), // просто очищаем тип от квалификатора
(void const*)&p // раз объект константный, то и результат константный
);
return const_cast< typename copy_constness<From,To>::type* >(p); // приводим к нужной константности
}
Написать метафункцию copy_constness — пара пустяков, хоть с boost/mpl, хоть без.
Все эмоциональные формулировки не соотвествуют действительному положению вещей и приведены мной исключительно "ради красного словца". За корректными формулировками и неискажённым изложением идей, следует обращаться к их автором или воспользоваться поиском