const или mutable?
От: unreg_flex  
Дата: 25.07.07 15:59
Оценка:
Итак, допустим имеется класс:

class A {
public:

  int& P() { return p; }

private:
  int p;
};


и функция:

void F(const A& a) {

  // some code ...

  int z=a.P();

  // some code ...

}


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

class A {
public:

  int& P() { return p; }
  const int& P() const { return p; }

private:
  int p;
};


Так вот, когда методов типа P(), появляется очень много, то задалбывает определять их парами.
В принципе так мы и делали, пока кое кто не предложил определять так:

class A {
public:

  int& P() const { return p; }

private:
  mutable int p;
};


В принципе это приводит к уменьшению ненужной писанины, а собственно, кто и как с этим борется?
Re: const или mutable?
От: NikeByNike Россия  
Дата: 25.07.07 16:33
Оценка: 1 (1)
Здравствуйте, unreg_flex, Вы писали:

А зачем вообще ссылки возвращать?
ИМХО это плохой приём. Иногда конечно бывают случаи когда это оправдано, но их довольно мало. Возвращать неконстантную ссылку на константное содержимое — не правильно и может быть опасно.
Использование mutable, в данном случае — тоже не корректно, он не для того нужен.
Нужно разобрать угил.
Re: const или mutable?
От: Кодт Россия  
Дата: 25.07.07 16:42
Оценка: +1 :)
Здравствуйте, unreg_flex, Вы писали:

_>Так вот, когда методов типа P(), появляется очень много, то задалбывает определять их парами.

_>В принципе так мы и делали, пока кое кто не предложил определять так:

(возвращать неконстантную ссылку на mutable данные)

_>В принципе это приводит к уменьшению ненужной писанины, а собственно, кто и как с этим борется?


Это приводит к снижению строгости.
Это, возможно, заставляет компилятор размещать статические константные объекты в секции данных, а не в секции констант.

Насчёт определения парами: может быть, задуматься о рефакторинге?
Ну, например,
class Foo
{
    int x_;
    int y_;
    int z_;
    .....
public:
    int& x() { return x_; }
    int const& x() const { return x_; }
    .....
    
    void doit(); // что-то полезное
    .....
};

// заменить на:
class FooNaked
{
public:
    // раз уж всё равно к ним есть прямой доступ
    int x_;
    int y_;
    int z_;
    .....
    void doit();
};
// или на
class FooPacked
{
public:
    // предоставить доступ ко всем таким полям одним махом
    struct Settings
    {
        int x, y, z;
    };
private:
    Settings s_;
public:
    Settings& settings() { return s_; }
    Settings const& settings() const { return s_; }
    .....
    void doit();
};

Если это какой-то опосредованный доступ (например, к элементам внутреннего контейнера) — определиться с необходимым минимумом, а если этот минимум слишком широк — то дать сразу доступ к этому контейнеру.
... << RSDN@Home 1.2.0 alpha rev. 655>>
Перекуём баги на фичи!
Re: const или mutable?
От: Константин Л. Франция  
Дата: 25.07.07 18:17
Оценка:
Здравствуйте, unreg_flex, Вы писали:

[]

_>Так вот, когда методов типа P(), появляется очень много, то задалбывает определять их парами.

_>В принципе так мы и делали, пока кое кто не предложил определять так:

_>
_>class A {
_>public:

_>  int& P() const { return p; }

_>private:
_>  mutable int p;
_>};
_>


_>В принципе это приводит к уменьшению ненужной писанины, а собственно, кто и как с этим борется?


Ты нарушаешь контракт. С одной стороны ты говоришь, что не меняешь внешнего состояния объекта, с другой стороны позволяешь это сделать невидимо для компилятора и разработчика. p является видимым свойством объекта, а ты позволяешь легально завуалировать его изменение.

Так что либо пару методов, либо как посоветовал Кодт.
Re[2]: const или mutable?
От: unreg_flex  
Дата: 26.07.07 05:30
Оценка:
Здравствуйте, Кодт, Вы писали:

К>Это приводит к снижению строгости.

К>Это, возможно, заставляет компилятор размещать статические константные объекты в секции данных, а не в секции констант.

Все так и есть, самому не нравится

К>Насчёт определения парами: может быть, задуматься о рефакторинге?

К>Ну, например,
К>
К>class Foo
К>{
К>    int x_;
К>    int y_;
К>    int z_;
К>    .....
К>public:
К>    int& x() { return x_; }
К>    int const& x() const { return x_; }
К>    .....
    
К>    void doit(); // что-то полезное
К>    .....
К>};

К>// заменить на:
К>class FooNaked
К>{
К>public:
К>    // раз уж всё равно к ним есть прямой доступ
К>    int x_;
К>    int y_;
К>    int z_;
К>    .....
К>    void doit();
К>};
К>// или на
К>class FooPacked
К>{
К>public:
К>    // предоставить доступ ко всем таким полям одним махом
К>    struct Settings
К>    {
К>        int x, y, z;
К>    };
К>private:
К>    Settings s_;
К>public:
К>    Settings& settings() { return s_; }
К>    Settings const& settings() const { return s_; }
К>    .....
К>    void doit();
К>};
К>

К>Если это какой-то опосредованный доступ (например, к элементам внутреннего контейнера) — определиться с необходимым минимумом, а если этот минимум слишком широк — то дать сразу доступ к этому контейнеру.

Везде где это возможно так и делается, просто я привел слишком простой пример.
Обычно ситуация примерно такая:

template< class T >
class InternalParameters {
public:

  T* Ptr() { return m_parameters; }
  T& P(int nIndex) {
    assert(nIndex>=0&&nIndex<4,"Incorrect index");
    return m_parameters[nIndex];
  }
  const T& P(int nIndex) const {
    assert(nIndex>=0&&nIndex<4,"Incorrect index");
    return m_parameters[nIndex];
  }

  T& AU() { return P(I_AU); }
  T& AV() { return P(I_AV); }
  T& U0() { return P(I_U0); }
  T& V0() { return P(I_V0); }
  T& K1() { return P(I_K1); }
  T& K2() { return P(I_K2); }
  T& P1() { return P(I_P1); }
  T& P2() { return P(I_P2); }

  const T& AU() const { return P(I_AU); }
  const T& AV() const { return P(I_AV); }
  const T& U0() const { return P(I_U0); }
  const T& V0() const { return P(I_V0); }
  const T& K1() const { return P(I_K1); }
  const T& K2() const { return P(I_K2); }
  const T& P1() const { return P(I_P1); }
  const T& P2() const { return P(I_P2); }

private:
  enum Indexes { I_AU,I_AV,I_U0,I_V0,I_K1,I_K2,I_P1,I_P2 };
  T m_parameters[4];
};


Обычно сразу забить порядок этих параметров не получается, поскольку в дальнейшем
m_parameters может принимать участие в какой-нибудь градиентной оптимизации и только тогда станет понятным
какими должны быть индексы I_AU,I_AV,...
Поэтому просто открыть поля тут неполучится.
Особенно раздражает необходимость дублировать методы типа P(...), т.к. их код бывает
достаточно большим, с многочисленными проверками (это тоже упрощенный пример).

Видимо придется оставить все как есть.

В любом случае спасибо за обсуждения.
Re: const или mutable?
От: np9mi7 Россия  
Дата: 26.07.07 05:46
Оценка:
Здравствуйте, unreg_flex, Вы писали:

_>Так вот, когда методов типа P(), появляется очень много, то задалбывает определять их парами.

_>В принципе это приводит к уменьшению ненужной писанины, а собственно, кто и как с этим борется?

Что бы не "задалбывало" писать код у которого нулевая логика — определи для этого кода snippet, и пусть твоя IDE этим занимается + присоединяюсь ко всем замечаниям по поводу ссылок на integral типы.
[RSDN@Home 1.1.4 stable SR1 rev. 568 on Windows XP 5.1.2600.0]
"В любое мгновение принятия решения, лучшее, что вы можете сделать, это принять правильное решение; следующим лучшим вариантом будет принять неправильное решение, худший вариант – не принимать решения совсем" (c) Теодор Рузвельт.
Re[3]: const или mutable?
От: Кодт Россия  
Дата: 26.07.07 10:42
Оценка:
Здравствуйте, unreg_flex, Вы писали:

<>

Можно попробовать сделать на макросах
template<class T>
class InternalParams
{
private:
    enum Index { I_AU, I_AV, ...., I__Count };
    void check(Index i) const { assert(i>=0 && i<I__Count); }
    
    T m_params[I__Count];
    
public: // или всё-таки private?
    T& at(Index i) { check(i); return m_params[i]; }
    T const& at(Index i) const { check(i); return m_params[i]; }

#define DEFPARAM(Id) \
    T& Id() { return at(I_##Id); } \
    T const& Id() const { return at(I_##Id); } \
    //endmacro
    
    DEFPARAM(AU)
    DEFPARAM(AV)
    .....
#undef DEFPARAM
};
... << RSDN@Home 1.2.0 alpha rev. 655>>
Перекуём баги на фичи!
Re: const или mutable?
От: MasterZiv СССР  
Дата: 27.07.07 09:55
Оценка:
unreg_flex пишет:

> В принципе это приводит к уменьшению ненужной писанины, а собственно,

> кто и как с этим борется?

IMHO не надо с этим бороться. Потому что последствия борьбы
будут страшнее того, с чем боролись. Лучше перегружать методы
по const/nonconst. Кроме того, пример-то дурацкий, а обычно в
реальных случаях у этих методов и семантика разная, что ведет
к их разной реализации.
Posted via RSDN NNTP Server 2.1 beta
Re: const или mutable?
От: demi США  
Дата: 27.07.07 10:31
Оценка:
Здравствуйте, unreg_flex, Вы писали:

_>Итак, допустим имеется класс:


_>
_>class A {
_>}
_>


Давайте подумаем, зачем требуются const T& и T& методы? Видтимо, один призван для того, чтобы получить значение, другой — чтобы установить. Можно попробовать оформить как свойство. Причем, будет корректно обрабатываться перегрузка по const/non-const класса держателя свойства. В случае, если у геттеров-сеттеров сложная логика поведения, можно передавать указатеь на ф-цию член, который займется обработкой. Пример:


// Намутить еще всякий ук-лей на функции геттеры сеттеры класса-держателя свойства, если логика не тривиальна...
template <class T> 
struct property
{
    property(T t = T()): _t(t)
    {
    }

    // Оператор позволяет взять значение. Для ленивых оформлен как преобразование, но я не сторонник этого.
    operator T() const
    {
        return _t;
    }

    property& operator= (const property& p) // сеттер
    {
        _t = p._t;
        return *this;
    }

private:
    T _t;
};

// Тетовый классец
struct A
{
    property<int> ip;
};

void f1(const A& a)
{
    int y = a.ip;
    // За что боролись: когда объект A - const проперти не может быть ничего присвоено. Только чтение.
    //a.ip = 10; //This is not allowed by compiler!
    printf("%i\n", y);
}

void f2(A& a)
{
    // А здесь все методы доступны
    int y = a.ip; //This allows!
    a.ip = 9;
}

void main()
{
    A a;
    f2(a);
    f1(a);
}
Не стыдно попасть в дерьмо, стыдно в нём остаться!
Re[2]: const или mutable?
От: demi США  
Дата: 27.07.07 10:34
Оценка: 3 (1)
Здравствуйте, demi, Вы писали:


...

// Тетовый классец
struct A
{
      // Нет занудных пар с перегрузкой!
    property<int> ip;
};

...


В общем идея думаю ясна
Не стыдно попасть в дерьмо, стыдно в нём остаться!
Re: const
От: Roman Odaisky Украина  
Дата: 28.07.07 09:42
Оценка:
Здравствуйте, unreg_flex, Вы писали:

Можно избежать дублирования кода так, если осторожно:

template <class T>
T const& constify(T const& x)
{
    return x;
}

template <class T>
T volatile const& constify(T volatile const& x)
{
    return x;
}

template <class T>
T& deconstify(T const& x)
{
    return const_cast<T &>(x);
}

template <class T>
T volatile& deconstify(T volatile const& x)
{
    return const_cast<T volatile &>(x);
}

// ---

class SomeClass
{
    X const& getX(. . .) const
    {
        assert(. . .);
        assert(. . .);

        if(. . .)
        {
            throw . . .;
        }

        // и т. д., и т. п.
        
        return someX;
    }

    X& getX(. . .)
    {
        return deconstify(constify(*this).getX(. . .));
    }
};

Поведение const_cast в такой ситуации полностью определено.
До последнего не верил в пирамиду Лебедева.
 
Подождите ...
Wait...
Пока на собственное сообщение не было ответов, его можно удалить.