Предполагая, что вы принимаете константную корректность как метод, я думаю, это означает, что вы предпочитаете константную корректность, проверенную компилятором, а не лаконичность. Итак, вы хотите, чтобы компилятор проверил две вещи:
- Когда "this" не является константой, вызывающий объект может безопасно изменить ссылочный номер возвращаемого вами указателя.
- Когда "this" является константным, любая работа, которую вы выполняете, не выполняет никаких неконстантных операций с "this".
Если версия с константой вызывает неконстантную, вы не получите (2). Если неконстантная версия вызывает константную и const_cast результат, то вы не получите (1). Например, предположим, что Bar
на самом деле char
, и код, который вы пишете, возвращает (в некоторых случаях) строковый литерал. Это будет компилироваться (и -Wwrite-strings не выдает предупреждения), но ваш вызывающий объект получает неконстантный указатель на строковый литерал. Это противоречит «вы предпочитаете, чтобы константная корректность проверялась компилятором».
Если они оба вызывают вспомогательную функцию-член Bar *getBar() const
, вы получаете и (1), и (2). Но если можно написать эту вспомогательную функцию, то зачем вы вообще возитесь с константными и неконстантными версиями, когда совершенно нормально изменять Bar, возвращаемый из const Foo? Иногда, возможно, некоторые детали реализации означают, что вы реализуете интерфейс с двумя средствами доступа, даже если вам нужен только один. В противном случае либо помощник не может быть записан, либо две функции могут быть заменены одним единственным помощником.
Пока размер кода не вызывает беспокойства, я думаю, что лучший способ достичь и (1), и (2) - это заставить компилятор действительно учитывать оба случая:
struct Bar { int a; };
struct Foo {
Bar *bar() { return getBar<Bar>(this); }
const Bar *bar() const { return getBar<const Bar>(this); }
Bar *bar2() const { return getBar<Bar>(this); } // doesn't compile. Good.
Bar *bar3() const { return getBar<const Bar>(this); } // likewise
private:
template <typename B, typename F>
static B *getBar(F *self) {
// non-trivial code, which can safely call other functions with
// const/non-const overloads, and we don't have to manually figure out
// whether it's safe to const_cast the result.
return &self->myBar;
}
Bar myBar;
};
Если код тривиален, например operator[]
, который обращается к некоторому массиву, принадлежащему объекту, то я бы просто продублировал код. В какой-то момент вышеупомянутый шаблон функции требует меньше усилий по кодированию, чем дублирование, и в этот момент используйте шаблон.
Я думаю, что подход const_cast, хотя и умный и, казалось бы, стандартный, просто бесполезен, потому что он предпочитает краткость проверенной компилятором корректности const. Если код в методе тривиален, вы можете его продублировать. Если это нетривиально, то вам или специалисту по сопровождению кода нелегко убедиться, что const_cast действительно действителен.
person
Steve Jessop
schedule
26.08.2009