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;
};
В принципе это приводит к уменьшению ненужной писанины, а собственно, кто и как с этим борется?
А зачем вообще ссылки возвращать?
ИМХО это плохой приём. Иногда конечно бывают случаи когда это оправдано, но их довольно мало. Возвращать неконстантную ссылку на константное содержимое — не правильно и может быть опасно.
Использование mutable, в данном случае — тоже не корректно, он не для того нужен.
Здравствуйте, 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();
};
Если это какой-то опосредованный доступ (например, к элементам внутреннего контейнера) — определиться с необходимым минимумом, а если этот минимум слишком широк — то дать сразу доступ к этому контейнеру.
[]
_>Так вот, когда методов типа P(), появляется очень много, то задалбывает определять их парами. _>В принципе так мы и делали, пока кое кто не предложил определять так:
_>
_>class A {
_>public:
_> int& P() const { return p; }
_>private:
_> mutable int p;
_>};
_>
_>В принципе это приводит к уменьшению ненужной писанины, а собственно, кто и как с этим борется?
Ты нарушаешь контракт. С одной стороны ты говоришь, что не меняешь внешнего состояния объекта, с другой стороны позволяешь это сделать невидимо для компилятора и разработчика. p является видимым свойством объекта, а ты позволяешь легально завуалировать его изменение.
Так что либо пару методов, либо как посоветовал Кодт.
Здравствуйте, Кодт, Вы писали:
К>Это приводит к снижению строгости. К>Это, возможно, заставляет компилятор размещать статические константные объекты в секции данных, а не в секции констант.
Все так и есть, самому не нравится
К>Насчёт определения парами: может быть, задуматься о рефакторинге? К>Ну, например, К>
К>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();
К>};
К>
К>Если это какой-то опосредованный доступ (например, к элементам внутреннего контейнера) — определиться с необходимым минимумом, а если этот минимум слишком широк — то дать сразу доступ к этому контейнеру.
Везде где это возможно так и делается, просто я привел слишком простой пример.
Обычно ситуация примерно такая:
Обычно сразу забить порядок этих параметров не получается, поскольку в дальнейшем
m_parameters может принимать участие в какой-нибудь градиентной оптимизации и только тогда станет понятным
какими должны быть индексы I_AU,I_AV,...
Поэтому просто открыть поля тут неполучится.
Особенно раздражает необходимость дублировать методы типа P(...), т.к. их код бывает
достаточно большим, с многочисленными проверками (это тоже упрощенный пример).
Здравствуйте, unreg_flex, Вы писали:
_>Так вот, когда методов типа P(), появляется очень много, то задалбывает определять их парами. _>В принципе это приводит к уменьшению ненужной писанины, а собственно, кто и как с этим борется?
Что бы не "задалбывало" писать код у которого нулевая логика — определи для этого кода snippet, и пусть твоя IDE этим занимается + присоединяюсь ко всем замечаниям по поводу ссылок на integral типы.
[RSDN@Home 1.1.4 stable SR1 rev. 568 on Windows XP 5.1.2600.0]
"В любое мгновение принятия решения, лучшее, что вы можете сделать, это принять правильное решение; следующим лучшим вариантом будет принять неправильное решение, худший вариант – не принимать решения совсем" (c) Теодор Рузвельт.
unreg_flex пишет:
> В принципе это приводит к уменьшению ненужной писанины, а собственно, > кто и как с этим борется?
IMHO не надо с этим бороться. Потому что последствия борьбы
будут страшнее того, с чем боролись. Лучше перегружать методы
по const/nonconst. Кроме того, пример-то дурацкий, а обычно в
реальных случаях у этих методов и семантика разная, что ведет
к их разной реализации.
Здравствуйте, 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);
}
Не стыдно попасть в дерьмо, стыдно в нём остаться!