Элегантное решение для дублирования, 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)
};