Как я удаляю дублирование кода между подобной константой и функциями членства неконстанты?

226
задан sharptooth 7 December 2014 в 23:07
поделиться

8 ответов

Да, возможно избежать дублирования кода. Необходимо использовать функцию членства константы, чтобы иметь логику и иметь функцию членства неконстанты, называют функцию членства константы и переделывают возвращаемое значение к ссылке неконстанты (или указатель, если функции возвращают указатель):

class X
{
   std::vector<Z> vecZ;

public:
   const Z& Z(size_t index) const
   {
      // same really-really-really long access 
      // and checking code as in OP
      // ...
      return vecZ[index];
   }

   Z& Z(size_t index)
   {
      // One line. One ugly, ugly line - but just one line!
      return const_cast<Z&>( static_cast<const X&>(*this).Z(index) );
   }

 #if 0 // A slightly less-ugly version
   Z& Z(size_t index)
   {
      // Two lines -- one cast. This is slightly less ugly but takes an extra line.
      const X& constMe = *this;
      return const_cast<Z&>( constMe.Z(index) );
   }
 #endif
};

ПРИМЕЧАНИЕ: важно, чтобы Вы сделали НЕ , помещает логику в функцию неконстанты и имеют вызов функции константы функция неконстанты - это может привести к неопределенному поведению. Причина состоит в том, что постоянный экземпляр класса снят как непостоянный экземпляр. Функция членства неконстанты может случайно изменить класс, который состояния стандарта C++ приведут к неопределенному поведению.

59
ответ дан 8 revs, 3 users 79% 23 November 2019 в 03:53
поделиться

Для подробного объяснения см., что заголовок "Избегает Дублирования в const и не - const Функция членства", на p. 23, в Объекте 3 "Использования const, когда это возможно", в Эффективный C++ , 3-й редактор Scott Meyers, ISBN-13: 9780321334879.

alt text

Вот (упрощенное) решение Meyers:

struct C {
  const char & get() const {
    return c;
  }
  char & get() {
    return const_cast<char &>(static_cast<const C &>(*this).get());
  }
  char c;
};

два броска и вызов функции могут быть ужасными, но это корректно. У Meyers есть полное объяснение почему.

173
ответ дан WilliamKF 23 November 2019 в 03:53
поделиться

статья This DDJ показывает способ использовать шаблонную специализацию, которая не требует, чтобы Вы использовали const_cast. Для такой простой функции это действительно не необходимо все же.

повышение:: any_cast (однажды, это больше не делает), использует const_cast от версии константы, называя версию неконстанты для предотвращения дублирования. Вы не можете наложить семантику константы на версию неконстанты хотя, таким образом, необходимо быть очень осторожны с этим.

В конце некоторое дублирование кода хорошо целый, эти два отрывка непосредственно друг на друге.

-1
ответ дан Greg Rogers 23 November 2019 в 03:53
поделиться

Как правило, функции членства, для которых Вам нужны константа и версии неконстанты, являются методами get и методами set. Большую часть времени они - остроты, таким образом, дублирование кода не является проблемой.

0
ответ дан Dima 23 November 2019 в 03:53
поделиться

Как насчет того, чтобы переместить логику в закрытый метод, и только сделать "получают ссылку и возврат" материал в методах get? На самом деле я был бы справедливо смущен помехами и бросками константы в простой функции метода get, и я буду считать это ужасным за исключением чрезвычайно редких обстоятельств!

3
ответ дан MP24 23 November 2019 в 03:53
поделиться

Более подробный, чем Meyers, но я мог бы сделать это:

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& для экземпляра константы, который является, почему это является частным. Закрытые методы могут повредить инварианты внешнего интерфейса (в этом случае, желаемый инвариант является "объектом константы, не может быть изменен через ссылки, полученные через него к объектам, которые это имеет -").

Примечание, что комментарии являются частью шаблона - интерфейс _getZ, определяет, что это никогда не допустимо для вызова его (кроме средств доступа, очевидно): нет никакого мыслимого преимущества для выполнения так так или иначе, потому что это - еще 1 символ для ввода и не приведет к меньшему или более быстрому коду. Вызов метода эквивалентен вызову одного из средств доступа с const_cast, и Вы не хотели бы делать это также. Если Вы волнуетесь по поводу совершения очевидных ошибок (и это - справедливая цель), то назовите его const_cast_getZ вместо _getZ.

Между прочим, я ценю решение Meyers. У меня нет философского возражения на него. Лично, тем не менее, я предпочитаю крошечный бит управляемого повторения и закрытый метод, который нужно только назвать в определенных плотно-контролируемых-условиях по методу, который похож на шум в линии. Выберите свой яд и придерживайтесь его.

[Редактирование: Kevin справедливо указал, что _getZ мог бы хотеть звонить, дальнейший метод (скажите, генерирует), который специализирован на константе таким же образом getZ. В этом случае _getZ видел бы константу Z& и имейте к const_cast его перед возвратом. Это все еще безопасно, так как шаблонное средство доступа управляет всем на основе политик, но не исключительно очевидно, что это безопасно. Кроме того, если Вы делаете это, и затем более позднее изменение генерирует, чтобы всегда возвратить константу, тогда также необходимо изменить getZ, чтобы всегда возвратить константу, но компилятор не скажет Вам, что Вы делаете.

, Что последняя точка о компиляторе также верна для рекомендуемого шаблона Meyers, но первая точка о неочевидном const_cast не. Так в итоге я думаю что, если _getZ, оказывается, нужен const_cast для его возвращаемого значения, то этот шаблон теряет много своего значения по Meyers. Так как это также переносит недостатки по сравнению с Meyers, я думаю, что переключился бы на его в той ситуации. Рефакторинг от одного до другого легок - это не влияет ни на какой другой допустимый код в классе, так как только недопустимый код и шаблон называют _getZ.]

22
ответ дан Steve Jessop 23 November 2019 в 03:53
поделиться

Вы могли также решить это с шаблонами. Это решение немного ужасно (но уродство скрыто в .cpp файле), но это действительно обеспечивает проверку компилятора constness и никакое дублирование кода.

файл ч:

#include <vector>

class Z
{
    // details
};

class X
{
    std::vector<Z> vecZ;

public:
    const std::vector<Z>& GetVector() const { return vecZ; }
    std::vector<Z>& GetVector() { return vecZ; }

    Z& GetZ( size_t index );
    const Z& GetZ( size_t index ) const;
};

.cpp файл:

#include "constnonconst.h"

template< class ParentPtr, class Child >
Child& GetZImpl( ParentPtr parent, size_t index )
{
    // ... massive amounts of code ...

    // Note you may only use methods of X here that are
    // available in both const and non-const varieties.

    Child& ret = parent->GetVector()[index];

    // ... even more code ...

    return ret;
}

Z& X::GetZ( size_t index )
{
    return GetZImpl< X*, Z >( this, index );
}

const Z& X::GetZ( size_t index ) const
{
    return GetZImpl< const X*, const Z >( this, index );
}

основной недостаток, который я вижу, - то, что, потому что вся сложная реализация метода находится в глобальной функции, любой необходимо овладеть членами X открытых методов использования как GetVector () выше (которых всегда должно быть константой и версией неконстанты), или Вы могли сделать эту функцию другом. Но мне не нравятся друзья.

[Редактирование: удаленный ненужный включают cstdio, добавленного во время тестирования.]

7
ответ дан Andy Balaam 23 November 2019 в 03:53
поделиться

Вот то, что я придумал:

class A
{
    int x;    
  public:
    MAYBE_CONST(
        CV int &GetX() CV {return x;}
    )

    //   Equivalent to:
    // int &GetX() {return x;}
    // const int &GetX() const {return x;}
};

аргумент MAYBE_CONST дублирован. В первой копии, CV ничем не заменяется; и во второй копии это заменяется const.

нет никакого предела на то, сколько раз CV может появиться в макро-аргументе.

<час>

Реализация:

#define MAYBE_CONST(...) IMPL_MC( ((__VA_ARGS__)) )
#define CV ))((

#define IMPL_MC(seq) \
    IMPL_MC_end(IMPL_MC_a seq) \
    IMPL_MC_end(IMPL_MC_const_0 seq)

#define IMPL_MC_identity(...) __VA_ARGS__
#define IMPL_MC_end(...) IMPL_MC_end_(__VA_ARGS__)
#define IMPL_MC_end_(...) __VA_ARGS__##_end

#define IMPL_MC_a(elem) IMPL_MC_identity elem IMPL_MC_b
#define IMPL_MC_b(elem) IMPL_MC_identity elem IMPL_MC_a
#define IMPL_MC_a_end
#define IMPL_MC_b_end

#define IMPL_MC_const_0(elem)       IMPL_MC_identity elem IMPL_MC_const_a
#define IMPL_MC_const_a(elem) const IMPL_MC_identity elem IMPL_MC_const_b
#define IMPL_MC_const_b(elem) const IMPL_MC_identity elem IMPL_MC_const_a
#define IMPL_MC_const_a_end
#define IMPL_MC_const_b_end
0
ответ дан 23 November 2019 в 03:53
поделиться
Другие вопросы по тегам:

Похожие вопросы: