Форсировать ли const в старом коде

Битый час сегодня спорили с коллегой по следующему вопросу. Имеем код:

void foo(T* t) {
  bar(t);
}

Проблема в том, что функция bar является legacy-функцией одной из наших старых библиотек, которую сейчас мы поменять не можем, и ее сигнатура: void bar(T*), то есть указатель-параметр не const. Но в реальности эта функция не меняет объект, на который указывает ее параметр.

Далее. Наш новый API, частью которого является foo, есть новейшая разработка, и должна быть спроектирована по уму. С точки зрения запланированной ответственности функции foo, она не должна менять объект, на который указывает t.

Я считаю, что код должен выглядеть так:

void foo(const T* t) {
  bar(const_cast<T*>(t));
}

Мои аргументы: так как контракт функции foo говорит, что эта функция не будет менять объект, на который указывает указатель, то этот факт должен быть отражен в API использованием слова const. И не имеет никакого значения, что по какой-то причине реализация этой функции внутри использует старый код, который неграмотно написан. Да, из-за это приходится делать некрасивое приведение типов, снимая const. Но эта некрасивость локализована внутри foo и в целом не оказывает влияния на стройность нового кода. Более того - если в будущем можно будет отказаться от использования старой функции bar, то проблема вообще исчезнет.

А вот контр-аргумент коллеги: может так случиться, что из-за ошибки в bar константный аргумент функции foo, которая по идее не должна менять аргумент, будет таки изменен, и получится крайне неприятный баг. В итого надо сделать аргумент функции foo НЕ const (и приведение будет уже не нужно), тем самым явно показать конечному пользователю нового API, что не стоит вообще рассчитывать на константность параметра функции foo.

Мы так и не договорились, так как у меня был «прогиб» в плане возможного нарушения константности вне зависимости, что там есть красивый const, а в подходе коллеги было не ясно, как объяснить в документации по функции foo почему и как она может менять свой аргумент - сказать, что мол из-за особенностей реализации foo мы не можем гарантировать константность? Получается, что мы проблемой старого кода портим дизайн нового API.

Дилемма.

P.S. Есть еще один изотерический вариант: внутри foo делать глубокое копирование объекта T и уже его передавать по неконстантному указателю в bar. Лично я, если надо выбирать между быстрым, но «плохим» кодом, и медленным, но со стройным дизайном, чаще выбираю второе, так как завтра купленный более быстрый сервер ускорит хороший, но медленный код, но не сделает плохой код более понятным.


Оригинальный пост | Disclaimer

Комментарии