Подтвердить что ты не робот

Превращает ли этот код в систему типа С++?

Я понимаю, что наличие метода const в С++ означает, что объект доступен только для чтения с помощью этого метода, но в противном случае он может измениться.

Однако этот код, по-видимому, изменяет объект с помощью ссылки const (т.е. методом const).

Является ли этот код законным в С++?

Если это так: нарушает ли это const -ness системы типов? Почему/почему нет?

Если нет: почему бы и нет?

Примечание 1: Я немного изменил этот пример, поэтому ответы могут ссылаться на более старые примеры.

Изменить 2: Очевидно, вам даже не нужен С++ 11, поэтому я удалил эту зависимость.

#include <iostream>

using namespace std;

struct DoBadThings { int *p; void oops() const { ++*p; } };

struct BreakConst
{
    int n;
    DoBadThings bad;
    BreakConst() { n = 0; bad.p = &n; } 
    void oops() const { bad.oops(); }  // can't change itself... or can it?
};

int main()
{
    const BreakConst bc;
    cout << bc.n << endl;   // 0
    bc.oops();              // O:)
    cout << bc.n << endl;   // 1

    return 0;
}

Обновление:

Я переместил лямбда в список инициализации конструктора, так как это позволяет мне впоследствии сказать const BreakConst bc;, который - поскольку bc сам теперь const (а не просто указатель) - казалось бы, подразумевает (от Stroustrup), который каким-либо образом изменяет bc после того, как построение должно привести к поведению undefined, хотя конструктор и вызывающий объект не могли бы знать это не видя определения друг друга.

4b9b3361

Ответ 1

Метод oops() не может изменять константу объекта. Кроме того, он этого не делает. Его анонимная функция делает это. Эта анонимная функция не находится в контексте объекта, а в контексте метода main(), который позволяет изменять объект.

Ваша анонимная функция не меняет этот указатель oops() (который определяется как const и поэтому не может быть изменен), а также никоим образом не выводит некоторую неконстантную переменную из этого указателя. Сам он не имеет этого указателя. Он просто игнорирует этот указатель и изменяет переменную bc основного контекста (который передается как параметр для вашего закрытия). Эта переменная не const и поэтому может быть изменена. Вы также можете передать любую анонимную функцию, изменяющую полностью несвязанный объект. Эта функция не знает, что она меняет объект, который ее сохраняет.

Если вы объявите его как

const BreakConst bc = ...

тогда основная функция также будет обрабатывать его как объект const и не может его изменить.

Изменить: Другими словами: атрибут const привязан к конкретному l-значению (ссылке), обращающемуся к объекту. Он не связан с самим объектом.

Ответ 2

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

В общем случае такие случаи не подрывают систему типов, поскольку система типов в С++ официально не гарантирует, что вы не можете изменять объект const или ссылку на const. Однако модификация объекта const является поведением undefined.

Из [7.1.6.1] cv-квалификаторы:

Указатель или ссылка на cv-quali-fi тип не должны фактически указывать или ссылаться на cv-квалификационный объект, но он рассматривается так, как если бы он это делал; const-quali fied access path не может использоваться для изменения объекта, даже если объект, на который делается ссылка, является неконстантным объектом и может быть модифицирован через другой путь доступа.

За исключением того, что любой член класса, объявленный изменчивым (7.1.1), может быть изменен, любая попытка изменить объект const в течение его жизненного цикла (3.8) в неустановленном поведении.

Ответ 3

Я уже видел нечто подобное. В основном вы вызываете функцию стоимости, которая вызывает что-то другое, которое модифицирует объект, не зная его.

Рассмотрим также:

#include <iostream>
using namespace std;

class B;

class A
{
    friend class B;
    B* pb;
    int val;
public:
    A(B& b); 
    void callinc() const;
    friend ostream& operator<<(ostream& s, const A& a)
    { return s << "A value is " << a.val; }
};

class B
{
    friend class A;
    A* pa;
public:
    void incval() const { ++pa->val; }
};

inline A::A(B& b) :pb(&b), val() { pb->pa = this; }
inline void A::callinc() const { pb->incval(); }


int main()
{
    B b;
    const A a(b);  // EDIT: WAS `A a(b)`
    cout << a << endl;
    a.callinc();
    cout << a << endl;
}

Это не С++ 11, но делает то же самое: Дело в том, что const не является транзитивным.

callinc() не меняет себя a и incval не меняет b. Обратите внимание, что в main вы можете даже объявить const A a(b); вместо A a(b); и все скомпилировать то же самое.

Это работает десятилетиями, и в вашем примере вы просто делаете то же самое: просто вы заменили класс B лямбдой.

ИЗМЕНИТЬ

Изменен основной(), чтобы отразить комментарий.

Ответ 4

Проблема - одна из логических констант по сравнению с побитовой константой. Компилятор ничего не знает о логическом значении вашей программы и только принудительно выполняет побитовое сост. Это для вас, чтобы реализовать логический const. Это означает, что в таких случаях, как вы показываете, если указана на память логически часть объекта, вы должны воздержаться от его модификации в const, даже если компилятор позволит вам (поскольку он не является частью побитового изображения объекта). Это также может означать, что если часть побитовое изображение объекта не является частью логического значения объект (например, встроенный счетчик ссылок или кешированные значения), вы делаете это mutable, или даже отбросить const, в тех случаях, когда вы изменяете его без изменяя логическое значение объекта.

Ответ 5

Функция const просто помогает избежать случайного злоупотребления. Он не предназначен для предотвращения взлома специализированного программного обеспечения. Это то же самое, что и частное и защищенное членство, кто-то всегда может принимать адрес объекта и увеличивать его по памяти для доступа к внутренним классам, нет возможности остановить его.

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