Немного более многословно, чем Мейерс, но я мог бы сделать это:
class X {
private:
// This method MUST NOT be called except from boilerplate accessors.
Z &_getZ(size_t index) const {
return something;
}
// boilerplate accessors
public:
Z &getZ(size_t index) { return _getZ(index); }
const Z &getZ(size_t index) const { return _getZ(index); }
};
Закрытый метод обладает нежелательным свойством, что он возвращает неконстантный Z & для константного экземпляра, поэтому он является закрытым. Приватные методы могут нарушать инварианты внешнего интерфейса (в этом случае желаемый инвариант - «объект const нельзя изменить с помощью ссылок, полученных через него, на объекты, которые он имеет -a»).
Обратите внимание, что комментарии являются частью шаблона. Интерфейс _getZ указывает, что его вызов никогда не будет действительным (за исключением средств доступа, очевидно): в любом случае это не представляется возможным, потому что это еще 1 символ для ввода и не приведет к меньшему или более быстрому коду. Вызов метода эквивалентен вызову одного из методов доступа с const_cast, и вам бы этого тоже не хотелось. Если вы беспокоитесь о том, чтобы сделать ошибки очевидными (и это справедливая цель), тогда назовите это const_cast_getZ вместо _getZ.
Кстати, я ценю решение Мейерса. У меня нет философских возражений против этого. Лично я предпочитаю немного контролируемого повторения и частный метод, который должен вызываться только в определенных строго контролируемых обстоятельствах, а не метод, похожий на шум в линии. Выбери свой яд и придерживайся его.
[Редактировать: Кевин справедливо указал, что _getZ может захотеть вызвать еще один метод (скажем, generateZ), который является const-специализированным таким же образом, как getZ. В этом случае _getZ увидит const Z & и его придется const_cast перед возвратом. Это все еще безопасно, так как шаблонный аксессуар контролирует все, но не совсем очевидно, что это безопасно. Более того, если вы сделаете это, а затем измените generateZ, чтобы он всегда возвращал const, вам также нужно изменить getZ, чтобы он всегда возвращал const, но компилятор не скажет вам, что вы это делаете.
Это последнее замечание о компиляторе также верно для рекомендованного Мейерсом шаблона, но первое замечание о неочевидном const_cast - нет. Итак, в итоге я думаю, что если _getZ окажется нужным const_cast для своего возвращаемого значения, то этот шаблон теряет большую часть своего значения по сравнению с Мейерсом. Так как он также страдает недостатками по сравнению с Мейерсом, я думаю, что я бы переключился на его в этой ситуации. Рефакторинг от одного к другому прост - он не влияет на любой другой допустимый код в классе, так как только недопустимый код и шаблон вызывают _getZ.]