Использование const_cast для добавления константы - плохая идея?

как мы все знаем, следует избегать использования const_cast для удаления константности указателя.

Но как же быть наоборот?

Для моего варианта использования у меня есть функция, которая копирует данные (байты) из неконстантного исходного буфера. Я подумал, что хорошим дизайнерским решением было бы объявить параметр в соответствии с этим исходным буфером полностью константным.

void copyfunction(const char* const data) { ... }

Для вызова функции, как показано ниже, это приведет к ошибке типа указателя 'const char * const ‹-> char *'.

void main() {
    char sourcebuffer[] = {0x00};

    copyfunction(sourcebuffer);
}

Конечно, теперь я мог бы просто объявить sourcebuffer как const, но в моем случае у меня нет доступа к этой переменной, потому что она находится в другом месте кода (внешняя библиотека).

void main() {
    char sourcebuffer[] = {0x00};

    copyfunction(const_cast<const char* const>(sourcebuffer));
}

Однако следующий код будет работать, но хороший ли это стиль (в соответствии с моим вариантом использования)?

Я думал, что объявление параметра copyfunction как const гарантирует пользователю не изменять (только для чтения) указатель или расположение самого исходного буфера. Таким образом, в этом случае const_cast будет только необходимым злом для включения вызова функции, а не для преднамеренного удаления константности указателя ...

Приветствует


person Steve Murdock    schedule 06.01.2014    source источник
comment
В этом случае использования ideone.com/BcI7Uq он компилируется (как только вы сделаете main return int).   -  person Yakk - Adam Nevraumont    schedule 06.01.2014
comment
Зачем вам нужно явно приводить его? constness будет за вас. И тип возврата main - int, а не void.   -  person Praetorian    schedule 06.01.2014
comment
В этом нет необходимости. Неконстантная переменная может быть неявно преобразована в константу.   -  person Zac Howland    schedule 06.01.2014
comment
Ага! Очень полезно! Извините, я еще не знал этого наверняка ... Так что логично, что вы можете найти только фрагменты кода const_cast, где он используется для удаления константности. Спасибо!   -  person Steve Murdock    schedule 06.01.2014
comment
@SteveMurdoc есть ситуации, когда const_cast добавляет constness, но это опасно: например, от int** до int const**. См. Здесь: ideone.com/tcByRQ   -  person Yakk - Adam Nevraumont    schedule 06.01.2014


Ответы (2)


Вы не должны использовать const_cast для добавления const, потому что:

  1. это не нужно. T* неявно преобразуется в const T*. В вашем вопросе указано, что char sourcebuffer[] = {0x00}; copyfunction(sourcebuffer); является ошибкой, но это неправда.

  2. это потенциально (хотя и маловероятно) вредно. Он может удалить volatile из типа указателя, что не является намерением здесь и привело бы к неопределенному поведению, если бы sourcebuffer был объявлен как volatile sourcebuffer[].

person Steve Jessop    schedule 06.01.2014
comment
Привет, а как насчет добавления константы для вызова функции константного класса? rextester.com/EFGPC9868 - person rikimaru2013; 01.12.2016

Вы не должны использовать const_cast для добавления const, потому что

  1. В тех случаях, когда операция безопасна, она почти всегда не требуется. int* неявно превращается в const int*.

  2. Он может делать то, чего вы не хотите. Он может удалить volatile или заставить вас упустить тот факт, что const был добавлен где-то еще в ваших переменных, а ваш const_cast теперь молча их удаляет.

  3. В тех случаях, когда требуется добавить const, его использование опасно труднопроходимыми способами.

Бывают случаи, когда вам нужно вызвать const_cast, чтобы добавить const, чего не будет неявно.

void assign_ptr( int const*& lhs, int const* rhs ) { lhs = rhs; }
int const foo = 7;
int* bar = nullptr;
assign_ptr( const_cast<int const*&>(bar), &foo );
*bar = 2; // undefined behavior!
std::cout << foo << "@" << &foo << "\n"; // will print probably 7@something
std::cout << *bar << "@" << bar << "\n"; // will print probably 2@same address as above!

приведенный выше вызов assign_ptr только добавляет const, но неявно этого не произойдет.

Побочным эффектом этого является то, что модификация *bar является неопределенным поведением, поскольку она изменяет объявленную переменную const (это делает bar, int*, указывающим на foo a const int).

Таким образом, хотя для компиляции вызова assign_ptr требуется const_cast, это потому, что это было небезопасно. const_cast не делает его безопаснее, он просто скрывает ошибку.

Это частный случай задачи прямоугольник-квадрат. Квадраты не являются прямоугольниками, потому что, если вы измените ширину квадрата, его высота также изменится, а этого не произойдет, когда вы измените прямоугольник. Точно так же int** не int const**. (Обратите внимание, что неизменяемые квадраты представляют собой своего рода неизменяемый прямоугольник; проблема возникает из-за мутации. В случае указателей int*const* является int const*const*: изменчивость указателей «более высокого уровня» вызывает проблему.)

person Yakk - Adam Nevraumont    schedule 06.01.2014
comment
Уверен, я уже знал, что прямоугольники не квадраты. (Во всяком случае, не все прямоугольники.) Я думаю, что менее очевидно то, что квадраты не являются прямоугольниками ... - person Nemo; 07.01.2014
comment
@ Немо Я, баг PEBKAC. Фиксированный. - person Yakk - Adam Nevraumont; 07.01.2014
comment
@Yakk boost :: optional ‹ptree const &› = get_child_optional (PATH); не работает по той же причине в моем коде, насколько я могу понять. Я пытаюсь заявить о своем намерении не изменять ptree в моем коде. Какой правильный подход? Поскольку проблема, вероятно, во мне, я бы хотел изменить свое мышление. Кстати, почему int * const * == int const * const *? Я не сталкивался с этим раньше. - person batbrat; 20.02.2017