Доброго дня.
Раньше как-то сильно не задумывался и негласно для себя считал, что const-спецификатор метода гарантирует неизменность полей объекта, из которого этот метод вызывается. А вот когда начал работать с сигнал-слот-архитектурой, увидел, что это, вообще говоря, не так:
class C_B;
////////////////////////////
//// C_A ////
////////////////////////////class C_A
{
C_B* m_pB;
int m_x;
public:
//------------------------------------------void set_pB(C_B* pB){m_pB = pB;}
//------------------------------------------void set_x(int x) {m_x = x;}
//------------------------------------------void foo_A(void)const;// несмотря на const, вызов этой функции может изменить значение поля m_x
//------------------------------------------
};
////////////////////////////
////////////////////////////
//// C_B ////
////////////////////////////class C_B
{
C_A* m_pA;
public:
//------------------------------------------
C_B(C_A* pA):m_pA(pA){}
//------------------------------------------void foo_B(void){ m_pA->set_x(10);}
//------------------------------------------
};
////////////////////////////
//------------------------------------------void C_A::foo_A(void)const
{
m_pB->foo_B();
}
//------------------------------------------void main(void)
{
C_A A;
C_B B(&A);
A.set_pB(&B);
A.set_x(0);
A.foo_A();
}
Потому встал вопрос, есть ли в других языках или в новом стандарте с++ что-нибудь наподобие "class const — метода", который делает
константными ЛЮБЫЕ this данного класса в своей области видимости, и тем самым предотвращает всякое изменение любого (а значит и заданного)объекта данного класса?
Это понадобилось, в частности, для того, чтобы на этапе компиляции отлавливать зацикливания. Например,
если foo_A воспринимаеть как сигнал о модификации объекта, к которому приконнекчен слот foo_B, то хотелось бы иметь гарантиии, что вызов этого слота ен приведет к повторному изменению объекта и повторному вызову сигнала (тем самым входя в цикл).
Re: "class const - метод" - можно ли организовать?
Здравствуйте, _hum_, Вы писали:
__> Раньше как-то сильно не задумывался и негласно для себя считал, что const-спецификатор метода гарантирует неизменность полей объекта, из которого этот метод вызывается.
Я, как сишник, для себя считаю, что const-спецификатор есть в первую очередь указание оптимизатору, что некоторый регион памяти не изменяется в течение жизни указателя. И если он изменяется по какой-то причине, то получаем UB. Истина математика всегда делать самые слабые утверждения А как оно в стандарте
Реализовать твоё поведение вряд ли возможно в принципе. Потому как на каждую операцию записи нам необходимо доказать, что невозможно изменение содержимого любого константного указателя. Что нетривиально.
Re[2]: "class const - метод" - можно ли организовать?
Здравствуйте, VTT, Вы писали:
VTT>У вас тут const-ность нарушается из-за наличия в классе не const ссылки на самого себя. Этот пример можно упростить до VTT>
да, спасибо, так нагляднее.
VTT>Если хочется гарантий, то можно делать immutable объекты.
вы не совсем тогда поняли — я хочу гарантию, что ВЫЗОВ ДАННОГО МЕТОДА не приведет к изменению данных, а не что эти данные ВОВСЕ не должны меняться.
и реализация этого вроде бы не сложная — компилятор должен все this-указатели данного класса, которые попадают в область видимости данного метода, считать const this. тогда бы он смог выдать ошибку в вашем примере, типа:
"m_p_non_const->m_x = x : использование неконстантного указателя на класс t_Something внутри class const метода"
Re[2]: "class const - метод" - можно ли организовать?
Здравствуйте, Mystic, Вы писали:
M>Здравствуйте, _hum_, Вы писали:
__>> Раньше как-то сильно не задумывался и негласно для себя считал, что const-спецификатор метода гарантирует неизменность полей объекта, из которого этот метод вызывается.
M>Я, как сишник, для себя считаю, что const-спецификатор есть в первую очередь указание оптимизатору, что некоторый регион памяти не изменяется в течение жизни указателя. И если он изменяется по какой-то причине, то получаем UB. Истина математика всегда делать самые слабые утверждения А как оно в стандарте
так одно другого не исключает мы говорим компилятору (который включает в себя и синтаксический анализатор, и оптимизатор), как мы считаем, должен себя вести объект, а уже компилятор использует эту инфу для многих целей — и для отлавливания семантических ошибок (сказали, что будем использовать без изменения, и почему-то пишем изменяющий код) и для оптимизации.
M>Реализовать твоё поведение вряд ли возможно в принципе. Потому как на каждую операцию записи нам необходимо доказать, что невозможно изменение содержимого любого константного указателя. Что нетривиально.
вроде бы несложно (см. пост выше для VTT)
Re[3]: "class const - метод" - можно ли организовать?
__>вы не совсем тогда поняли — я хочу гарантию, что ВЫЗОВ ДАННОГО МЕТОДА не приведет к изменению данных, а не что эти данные ВОВСЕ не должны меняться. __>и реализация этого вроде бы не сложная — компилятор должен все this-указатели данного класса, которые попадают в область видимости данного метода, считать const this. тогда бы он смог выдать ошибку в вашем примере, типа: __>"m_p_non_const->m_x = x : использование неконстантного указателя на класс t_Something внутри class const метода"
Это может быть и некоторый другой класс, откуда компилятору знать, куда будет указывать m_p_non_const в момент вызова метода?
Re[4]: "class const - метод" - можно ли организовать?
Здравствуйте, Mystic, Вы писали:
M>Здравствуйте, _hum_, Вы писали:
__>>вы не совсем тогда поняли — я хочу гарантию, что ВЫЗОВ ДАННОГО МЕТОДА не приведет к изменению данных, а не что эти данные ВОВСЕ не должны меняться. __>>и реализация этого вроде бы не сложная — компилятор должен все this-указатели данного класса, которые попадают в область видимости данного метода, считать const this. тогда бы он смог выдать ошибку в вашем примере, типа: __>>"m_p_non_const->m_x = x : использование неконстантного указателя на класс t_Something внутри class const метода"
M>Это может быть и некоторый другой класс, откуда компилятору знать, куда будет указывать m_p_non_const в момент вызова метода?
неважно, куда будет указывать. главное, какого он типа. а это определяется на этапе компиляции.
(речь не о динамических отлавливаниях изменений, когда, действительно, требуется рантайм-анализ указателей, а о статическом — мы просто тупо запрещаем в коде, попадающем в область видимости данного метода, менять что-то через указатели данного класса)
Re[3]: "class const - метод" - можно ли организовать?
Здравствуйте, _hum_, Вы писали:
__>вроде бы несложно (см. пост выше для VTT)
Например?
struct X
{
X * next;
void f() const { next->next = nullptr; }
};
void something(X * x);
int main()
{
X x;
something(&x); // Какими будут поля структуры X после выхода из метода?
x.f(); // Как понять, есть нарушение или нет?return 0;
}
#ifdef UB
void something(X * x)
{
x->next = x;
}
#else
void something(X * x)
{
x->next = new X();
}
#endif
Re[5]: "class const - метод" - можно ли организовать?
Здравствуйте, _hum_, Вы писали:
__>неважно, куда будет указывать. главное, какого он типа. а это определяется на этапе компиляции. __>(речь не о динамических отлавливаниях изменений, когда, действительно, требуется рантайм-анализ указателей, а о статическом — мы просто тупо запрещаем в коде, попадающем в область видимости данного метода, менять что-то через указатели данного класса)
Т. е. ты хочешь, чтобы некоторый superconst влиял на все экземпляры данного класса?
Re[5]: "class const - метод" - можно ли организовать?
Здравствуйте, _hum_, Вы писали:
__>(речь не о динамических отлавливаниях изменений, когда, действительно, требуется рантайм-анализ указателей, а о статическом — мы просто тупо запрещаем в коде, попадающем в область видимости данного метода, менять что-то через указатели данного класса)
Ну можно попробовать использовать не указатели, а специальный reference_wrapper класс, который бы давал доступ к изменяемому объекту только если он сам не const.
т.е. имел бы два метода (в отличие от стандартного)
T & get();
T const & get() const;
Говорить дальше не было нужды. Как и все космонавты, капитан Нортон не испытывал особого доверия к явлениям, внешне слишком заманчивым.
Re[6]: "class const - метод" - можно ли организовать?
Здравствуйте, Mystic, Вы писали:
M>Здравствуйте, _hum_, Вы писали:
__>>неважно, куда будет указывать. главное, какого он типа. а это определяется на этапе компиляции. __>>(речь не о динамических отлавливаниях изменений, когда, действительно, требуется рантайм-анализ указателей, а о статическом — мы просто тупо запрещаем в коде, попадающем в область видимости данного метода, менять что-то через указатели данного класса)
M>Т. е. ты хочешь, чтобы некоторый superconst влиял на все экземпляры данного класса?
ну, можно и так сказать (только не на сами экземпляры, а на указатели на эти экземпляры, и не на все указатели, а на те, что по коду попадают в область видимости данного superconst- метода)
Re[4]: "class const - метод" - можно ли организовать?
Здравствуйте, Mystic, Вы писали:
M>Здравствуйте, _hum_, Вы писали:
__>>вроде бы несложно (см. пост выше для VTT)
M>Например?
M>
M>struct X
M>{
M> X * next;
M> void f() const { next->next = nullptr; }
M>};
M>void something(X * x);
M>int main()
M>{
M> X x;
M> something(&x); // Какими будут поля структуры X после выхода из метода?
M> x.f(); // Как понять, есть нарушение или нет?
M> return 0;
M>}
M>#ifdef UB
M> void something(X * x)
M> {
x->>next = x;
M> }
M>#else
M> void something(X * x)
M> {
x->>next = new X();
M> }
M>#endif
M>
если под
void f() const { next->next = nullptr; }
понималось
void f() class_const { next->next = nullptr; }
то компилятор уже в этом месте не пропустит, ибо у вас в области видимости class_const-метода осуществляется изменение через указатель на данный класс.
иными словами, компилятор трактует это как:
Здравствуйте, _hum_, Вы писали:
__> Доброго дня. __> Раньше как-то сильно не задумывался и негласно для себя считал, что const-спецификатор метода гарантирует неизменность полей объекта, из которого этот метод вызывается. А вот когда начал работать с сигнал-слот-архитектурой, увидел, что это, вообще говоря, не так:
Авторы не захотели прописать mutable напротив поля m_x (или закладывались на какие-то компиляторы без поддержки оного), вот и делается такой "хак себя", когда весь класс неизменный, но определённое поле — эдакая temp-переменная, вроде как захваченная в контекст.
Здравствуйте, VTT, Вы писали:
VTT>Здравствуйте, _hum_, Вы писали:
__>>(речь не о динамических отлавливаниях изменений, когда, действительно, требуется рантайм-анализ указателей, а о статическом — мы просто тупо запрещаем в коде, попадающем в область видимости данного метода, менять что-то через указатели данного класса)
VTT>Ну можно попробовать использовать не указатели, а специальный reference_wrapper класс, который бы давал доступ к изменяемому объекту только если он сам не const. VTT>т.е. имел бы два метода (в отличие от стандартного) VTT>
VTT>T & get();
VTT>T const & get() const;
VTT>
а разве это может спасти от ситуации с моим первым примером, когда C_B не использует (ибо его никто не может обязать это делать) такой враппер для хранения ссылки на объект C_A?
Re[2]: "class const - метод" - можно ли организовать?
Здравствуйте, Mr.Delphist, Вы писали:
MD>Здравствуйте, _hum_, Вы писали:
__>> Доброго дня. __>> Раньше как-то сильно не задумывался и негласно для себя считал, что const-спецификатор метода гарантирует неизменность полей объекта, из которого этот метод вызывается. А вот когда начал работать с сигнал-слот-архитектурой, увидел, что это, вообще говоря, не так:
MD>Авторы не захотели прописать mutable напротив поля m_x (или закладывались на какие-то компиляторы без поддержки оного), вот и делается такой "хак себя", когда весь класс неизменный, но определённое поле — эдакая temp-переменная, вроде как захваченная в контекст.
MD>Собственно, ничего нового в явлении нет, идиома известна ещё со времён plain C, равно как и грабли ей сопутствующие http://stackoverflow.com/questions/98650/what-is-the-strict-aliasing-rule
не совсем понял, о чем вы. мютабельность полей тут не при чем.
Re[7]: "class const - метод" - можно ли организовать?
Здравствуйте, VTT, Вы писали:
VTT>Ну если уж вы часть программы не контролируете, то никакой superconst тут не поможет.
нет, речь о другом — в самом классе C_A я могу проконтролировать, чтобы все указатели на себя использовались только через такие врапперы, но за пределами этого класса — нет. и глупо рассчитывать на то, что использовать объекты этого класса будут только через врапперы (ну разве только специально писать инструкцию пользования данным классом, что довольно дико).
а компилятор мог бы это автоматом все отслеживать и говорить, где что не так...
Re[9]: "class const - метод" - можно ли организовать?
Здравствуйте, _hum_, Вы писали:
__>а компилятор мог бы это автоматом все отслеживать и говорить, где что не так...
И как бы он мог это отслеживать? Информации о том, указывает ли указатель в классе C_B на тот же экземпляр класса C_A, что и this в методе foo_A у него в принципе нет. В приведенном примере это очевидно только из-за крайнего упрощения.
Говорить дальше не было нужды. Как и все космонавты, капитан Нортон не испытывал особого доверия к явлениям, внешне слишком заманчивым.
Re[10]: "class const - метод" - можно ли организовать?
Здравствуйте, VTT, Вы писали:
VTT>Здравствуйте, _hum_, Вы писали:
__>>а компилятор мог бы это автоматом все отслеживать и говорить, где что не так...
VTT>И как бы он мог это отслеживать? Информации о том, указывает ли указатель в классе C_B на тот же экземпляр класса C_A, что и this в методе foo_A у него в принципе нет. В приведенном примере это очевидно только из-за крайнего упрощения.
нет. и вы тоже не поняли значит. речь не о том, чтобы не модифицировать ЗАДАННЫЙ ЭКЗЕМПЛЯР, а о том, чтобы запретить модификацию ВСЕ ЭКЗЕМПЛЯРЫ, если код их модификации попадает "в нитку", которая запускается вызовом class_const-метода.
иными словами компилятор должен действовать так:
0) заводит стек функций
1) помещает в стек class_const-метод
2) пока стек не пуст, берет верхний элемент стека и для него анализирует тело функции:
2.1) все указатели типа C_A* трактует как const C_A* и выдает ошибку, если с помощью них производится попытка модификации;
2.2) если встретилась функция, то помещает в стек и переходит к 2);
2.3) делает pop для стека.
3) анализ завершен
(тут еще оговорки, конечно, для рекурсивных вызовов, но это тоже решаемо на этапе анализа — просто не надо добавлять в стек то, что уже раньше встречалось)
Re[11]: "class const - метод" - можно ли организовать?
Здравствуйте, _hum_, Вы писали:
__>нет. и вы тоже не поняли значит. речь не о том, чтобы не модифицировать ЗАДАННЫЙ ЭКЗЕМПЛЯР, а о том, чтобы запретить модификацию ВСЕ ЭКЗЕМПЛЯРЫ, если код их модификации попадает "в нитку", которая запускается вызовом class_const-метода.
__>иными словами компилятор должен действовать так:
__>0) заводит стек функций __>1) помещает в стек class_const-метод __>2) пока стек не пуст, берет верхний элемент стека и для него анализирует тело функции: __> 2.1) все указатели типа C_A* трактует как const C_A* и выдает ошибку, если с помощью них производится попытка модификации; __> 2.2) если встретилась функция, то помещает в стек и переходит к 2); __> 2.3) делает pop для стека. __>3) анализ завершен
__>(тут еще оговорки, конечно, для рекурсивных вызовов, но это тоже решаемо на этапе анализа — просто не надо добавлять в стек то, что уже раньше встречалось)
Запретить модификацию всех экземпляров можно в runtime — сделать в классе дополнительное статическое поле со счетчиком const обращений и кидать исключение, если при вызове не-const метода этот счетчик не 0.
А на этапе компиляции это сделать не получится, так как у компилятора может не быть реализации класса C_B (или это вообще голый интерфейс), и, соответственно, он не сможет определить наличие обращение к не-const экземплярам C_A в нем.
Говорить дальше не было нужды. Как и все космонавты, капитан Нортон не испытывал особого доверия к явлениям, внешне слишком заманчивым.