Элегантное решение для дублирования, const и non-const, getters?
Не ненавидишь, когда у тебя
class Foobar {
public:
Something& getSomething(int index) {
// big, non-trivial chunk of code...
return something;
}
const Something& getSomething(int index) const {
// big, non-trivial chunk of code...
return something;
}
}
Мы не можем реализовать любой из этих методов с другим, потому что вы не можете вызывать версию const
из версии const
(ошибка компилятора).
Для приведения версии const
из не-t21 > требуется отрисовка.
Есть ли настоящее элегантное решение для этого, если нет, то, что ближе всего к нему?
Ответы
Ответ 1
Я помню из одной из эффективных книг на C++, что способ сделать это - реализовать неконстантную версию, отбросив const из другой функции.
Это не особенно красиво, но безопасно. Поскольку функция-член, вызывающая его, не является константой, сам объект не является константой, и отбрасывание константы разрешено.
class Foo
{
public:
const int& get() const
{
//non-trivial work
return foo;
}
int& get()
{
return const_cast<int&>(static_cast<const Foo*>(this)->get());
}
};
Ответ 2
Как насчет:
template<typename IN, typename OUT>
OUT BigChunk(IN self, int index) {
// big, non-trivial chunk of code...
return something;
}
struct FooBar {
Something &getSomething(int index) {
return BigChunk<FooBar*, Something&>(this,index);
}
const Something &getSomething(int index) const {
return BigChunk<const FooBar*, const Something&>(this,index);
}
};
Очевидно, что у вас все еще будет дублирование объектного кода, но нет дублирования исходного кода. В отличие от подхода const_cast, компилятор проверяет вашу const-правильность для обеих версий метода.
Вероятно, вам нужно объявить два интересных экземпляра BigChunk в качестве друзей этого класса. Это хорошее использование друга, так как функции друга скрыты рядом с другом, поэтому нет риска безусловной связи (ooh-er!). Но я не буду пытаться использовать синтаксис для этого прямо сейчас. Не стесняйтесь добавлять.
Скорее всего, BigChunk должен уважать себя, и в этом случае вышеприведенный порядок определения не будет работать очень хорошо, и для его сортировки потребуются некоторые форвардные декларации.
Кроме того, чтобы избежать множественного поиска BigChunk в заголовке и принятия решения о создании экземпляра и вызвать его, даже если он нравственно закрыт, вы можете переместить всю партию в файл cpp для FooBar. В анонимном пространстве имен. С внутренней связью. И знак, говорящий "остерегайся леопарда".
Ответ 3
Я бы включил const в неконстантный (второй вариант).
Ответ 4
Попробуйте устранить геттеры путем рефакторинга кода. Используйте функции друзей или классы, если только небольшое количество других вещей нуждается в чем-то.
Как правило, Getters и Setters прерывают инкапсуляцию, потому что данные попадают в мир. Использование друга предоставляет только данные нескольким, поэтому дает лучшую инкапсуляцию.
Конечно, это не всегда возможно, поэтому вы можете застрять с геттерами. По крайней мере, большинство или все "нетривиальные фрагменты кода" должны быть в одной или нескольких частных функциях, вызываемых обоими геттерами.
Ответ 5
Почему бы просто не вывести общий код в отдельную частную функцию, а затем вызвать два других вызова?
Ответ 6
Понятие "const" существует по какой-то причине. Для меня он устанавливает очень важный контракт, на основе которого написаны дальнейшие инструкции программы. Но вы можете сделать что-то по следующим строкам: -
- сделайте свой член "изменчивым"
- сделать 'getters' const
- возвращает неконстантную ссылку
При этом можно использовать ссылку const на LHS, если вам нужно поддерживать функцию const, когда вы используете геттер вместе с неконстантным использованием (опасным). Но бремя ответственности теперь на программиста на сохранение инвариантов класса.
Как уже говорилось в SO, изгнание константы изначально заданного объекта const и использование его - U.B. Поэтому я бы не использовал приведения. Также создание неконстантного объекта const, а затем снова отбрасывание константы выглядело бы не слишком хорошо.
Другим руководством по кодированию, которое я видел в некоторых командах, является: -
- Если переменную-член нужно изменить вне класса, всегда
верните указатель на него с помощью функции non-const member.
- Никакие функции-члены не могут возвращать неконстантные ссылки. Только const
ссылки разрешены в форме константных функций.
Это позволяет обеспечить некоторую согласованность в общей кодовой базе, и вызывающий может четко видеть, какие вызовы могут изменять переменную-член.
Ответ 7
Ссылка на объект const
имеет смысл (вы ограничиваете доступ только для чтения к этому объекту), но если вам нужно разрешить ссылку не const
, вы также можете сделать член общественности.
Я считаю, что это Скотт Майерс (Efficient С++).
Ответ 8
Я предлагаю использовать препроцессор:
#define ConstFunc(type_and_name, params, body) \
const type_and_name params const body \
type_and_name params body
class Something
{
};
class Foobar {
private:
Something something;
public:
#define getSomethingParams \
( \
int index \
)
#define getSomethingBody \
{ \
return something; \
}
ConstFunc(Something & getSomething, getSomethingParams, getSomethingBody)
};