Ответ 1
const_cast
также используется для удаления модификаторов volatile
, как это реализовано в этой (противоречивой) статье:
В качестве общего правила очень часто считается неправильной практикой использовать const_cast<>()
в коде на С++, поскольку он показывает (большую часть времени) недостаток в дизайне.
Пока я полностью согласен с этим, я, однако, задаюсь вопросом, в каких случаях использовалось const_cast<>()
, это нормально и единственное решение.
Не могли бы вы, ребята, дать мне несколько примеров, которые вы знаете/вы встретили?
Большое спасибо.
const_cast
также используется для удаления модификаторов volatile
, как это реализовано в этой (противоречивой) статье:
он в значительной степени предназначен для использования только с устаревшими API-интерфейсами, которые не являются корректными, т.е. с помощью функции, которую вы не можете изменить, которая имеет не const-интерфейс, но на самом деле не изменяет ничего на интерфейсе
Как и другие, основная цель состоит в том, чтобы удалить const
из объектов, чтобы перейти к неконстантным корректным функциям, которые, как вы знаете, не будут изменять аргумент.
Существует трюк (by Meyers?), чтобы избежать дублирования кода, и он выглядит следующим образом:
struct foo
{
const return_type& get(void) const
{
// fancy pants code that you definitely
// don't want to repeat
return theValue; // and got it
}
return_type& get(void)
{
// well-defined: Add const to *this,
// call the const version, then
// const-cast to remove const (because
// *this is non-const, this is ok)
return const_cast<return_type&>(static_cast<const foo&>(*this).get());
}
};
Я согласен с вашим утверждением, что его нормальное использование связано с тем, что вам нужно скрыть "недостаток дизайна".
IME - один из типичных сценариев использования, когда вы пытаетесь связать С++ с существующим C-кодом. Многие существующие C-коды принимают строки C как char *
, даже когда строка не изменяется, тогда как они обычно представляются как нечто, которое преобразуется в const char *
в С++. То, что импеданс несоответствует между двумя языками, которые вы обычно решаете с помощью const_cast. Конечно, вам лучше быть уверенным, что код, с которым вы взаимодействуете, не содержит каких-либо симпатичных идей об изменении передаваемых данных.
Я бы сказал, что он код пахнет во вновь написанном коде, но для взаимодействия со старым кодом C и С++ это необходимое зло. Тем не менее, я был бы крайне осторожен в отношении кода, который требует const_cast
для любых объектов, отличных от POD, поскольку это обычно проблема, которая должна решаться на уровне разработки, а не на уровне кода.
Одно законное использование (на мой взгляд) с итераторами std::set
. Они всегда const
, чтобы предотвратить изменение ключа, используемого в наборе. Изменение ключа нарушит внутреннюю структуру набора и вызовет поведение undefined.
Однако, пока ключ не изменяет его, безопасно изменять другие данные в объекте.
Скажем, у вас есть std::set
, как это:
std::set<MyObject> some_set;
И класс вроде этого:
class MyObject {
public:
MyObject(const std::string &key)
: key_(key) {}
bool operator<(const MyObject &other) const {
return key_ < other.key_;
}
private:
// ...
// <some other data>
// ...
const std::string key_;
};
В приведенном выше примере ключ уже const, поэтому, даже если вы изменяете объект, вы не можете сломать внутреннюю структуру набора.
Обычно вы можете получить ссылку const
из набора итератора:
const MyObject &object = *some_set_iterator;
Но поскольку ключ const
, он безопасен для const_cast
разыменованного итератора:
MyObject &object = const_cast<MyObject &>(*some_set_iterator);
Одно очень законное использование этого - когда у вас есть как const, так и non const api (для объектов const и non const соответственно), как в
class Bar {
const SomeType& foo() const;
SomeType& foo();
}
Тогда, поскольку мы не хотим дублировать код в обеих функциях, мы часто используем
class Bar {
SomeType& foo() {
//Actual implementation
}
const SomeType& foo() const {
return const_cast<Bar*>(this)->foo();
}
};
Это, конечно, предполагает, что foo не делает что-то, что нарушает семантику const.
Да, конечно, когда ваш код вызова, который вы не можете изменить и не является корректным. Следует отметить, что вы должны использовать его только с вызовами функций, которые, как вы знаете, не будут изменять ваши данные!