LINUX.ORG.RU

При передаче константного объекта по ссылке в функцию, в теле функции не возможно брать из него данные

 , ,


1

2

имеется пример:

#include <iostream>

using namespace std;

class Number
{
private:
    int number = 0;

public:
    int getNumber ()
    {return number;}

    void setNumber(const int number)
    {
        this->number = number;
    }

    Number & operator= (const Number &number)
    {
        this->number = number.number;
        return *this;
    }
};

class TwoNumbers
{
private:
    Number num1, num2;

public:
    void getNumbers(Number &num1, Number &num2)
    {
        num1 = this->num1;
        num2 = this->num2;
    }

    void setNumbers (const int num1, const int num2)
    {
        this->num1.setNumber(num1);
        this->num2.setNumber(num2);
    }
};

void upup (const TwoNumbers &numbers)
{
    Number a,b;

    numbers.TwoNumbers::getNumbers(a,b);

    cout << a.getNumber() << endl << b.getNumber() << endl;
}

int main()
{
    TwoNumbers c;
    c.setNumbers(1,2);

    upup(c);

    return 0;
}


в таком виде выдает ошибку:
../Test/main.cpp:49:39: error: passing ‘const TwoNumbers’ as ‘this’ argument discards qualifiers [-fpermissive]
     numbers.TwoNumbers::getNumbers(a,b);
                                       ^


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

не могу понять почему так происходит?

и еще — почему то если константу не убирать в параметре описания функции, то qt creator отказывается видить методы данного объекта (const TwoNumbers &numbers), однако видит их через два двоеточия и класс.

★★

Последнее исправление: safocl (всего исправлений: 3)
--- int getNumber ()
+++ int getNumber () const
--- void getNumbers(Number &num1, Number &num2)
+++ void getNumbers(Number &num1, Number &num2) const
XMs ★★★★★
()
Последнее исправление: XMs (всего исправлений: 1)
Ответ на: комментарий от safocl

Потому что так ты гарантируешь, что состояние объекта не поменяется. Почитать можно в Стандарте

XMs ★★★★★
()
Ответ на: комментарий от safocl

а почему так надо делать?

Фактически это задаёт тип переменной this - (const TwoNumbers *) или (TwoNumbers *). this - это почти как обычный аргумент функции.

где обб ентом прочитать?

В любой книжке где описаны плюсовые классы.

Deleted
()
Ответ на: комментарий от XMs

т.е. он думает, что надо защитить переменную «int number = 0», когда я беру из нее данные функцией «int getNumber ()» ?
но там ведь я же не ссылку или указатель ретурню, а только значение.

safocl ★★
() автор топика
Ответ на: комментарий от safocl

Он не «думает», у компилятора и так много забот. Он просто смотрит, что объект const, и позволяет использовать только const-методы. Если ты в const-методе будешь менять какие-то члены класса, он кинет ошибку

XMs ★★★★★
()
Ответ на: комментарий от safocl

Он просто смотрит, что объект const, и позволяет использовать только const-методы

Если метод не const, ты МОЖЕШЬ поменять их. Этого достаточно, чтобы кинуть ошибку

XMs ★★★★★
()
Ответ на: комментарий от safocl

Члены класса. Неважно, есть они у тебя или нет. Неважно, меняешь ты их, или нет. Функция не-const — компилятор считает, что есть, и ты их меняешь

XMs ★★★★★
()
Ответ на: комментарий от safocl

Повторяю:

Неважно, меняешь ты их, или нет. Функция не-const — компилятор считает, что [члены класса] есть, и ты их меняешь

Совершенно неважно, какие у тебя аргументы, я даже не уверен, что он на них смотрит. Если у const-объекта дёргаются не-const-методы, это ошибка

XMs ★★★★★
()
Ответ на: комментарий от XMs

ясно, понял в чем проблема, однако не понял почему так происходит, всем спс.
пойду ставить const везде))

safocl ★★
() автор топика
Ответ на: комментарий от safocl

однако не понял почему так происходит

Потому что это логично. Const — гарантия, что объект не поменяет своё состояние.


пойду ставить const везде

Не везде, а только там, где он нужен. Const не имеет смысла в сеттерах, конструкторах, деструкторах, статических функциях класса. И этим список не ограничивается

XMs ★★★★★
()
Ответ на: комментарий от XMs

Потому что это логично. Const — гарантия, что объект не поменяет своё состояние.

ну так я же гарантирую енто когда пишу «foo(const MyClass &obj)»

safocl ★★
() автор топика
Ответ на: комментарий от safocl

Кому? Ты это гарантируешь области видимости, откуда такая функция дёргается. И, кстати, правило то же самое — MyClass становится const MyClass — а значит, дёргать можно только const-методы, которые гарантируют уже этой функции, что состояние класса не поменяется. Всё логично

XMs ★★★★★
()
Ответ на: комментарий от XMs

Кому? Ты это гарантируешь области видимости, откуда такая функция дёргается.

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

safocl ★★
() автор топика
Ответ на: комментарий от XMs

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

safocl ★★
() автор топика
Ответ на: комментарий от safocl

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

С чего бы? Const — это не только гарантия неизменности, но и ограничение на вызов только const-методов. Иначе смысла в const не так много. Допустим, у объекта есть метод clear(), очевидно, не-const. Как компилятор должен понять, что вызов таких методов для const-объекта — это плохо?

XMs ★★★★★
()
Ответ на: комментарий от safocl

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

XMs ★★★★★
()
Ответ на: комментарий от safocl

На низком уровне это ничего не значит. В реальности в большинстве случаев const можно скастовать в не-const и делать с ним что-угодно. ОС, конечно, имеет механизмы защиты памяти, но они работают только на уровне страниц 4 КБ (то есть нельзя защитить от записи отдельные байты), а как секция данных, так и куча не имеют никакой защиты, только исполняемый код. К тому же, что ты передаёшь в качестве аргумента функции? Если это изначально не const, то он гарантированно будет в области памяти без защиты от записи. Менять права доступа к странице памяти при вызове const функции? Медленно и вызывает проблемы с многопоточностью.

Короче, в 99% случаев const существует исключительно для компилятора, чтобы выдавать ошибки компиляции (и, возможно, некоторые компиляторы могут на основе const делать какие-то оптимизации). В машинном коде от него ничего не остаётся и в рантайме ни на что не влияет. В 1% случаев const может таки на что-то влиять, но обычно это больше касается микроконтроллеров, где могут быть отдельные памяти для программ и для данных (при этом размер памяти для программ в разы превышает размер памяти для данных) и const задаёт куда положить объект в прошивке.

KivApple ★★★★★
()
Последнее исправление: KivApple (всего исправлений: 3)
Ответ на: комментарий от XMs

ну ты хочеш сказать, что :

class myclass
{
private:
int i = 0;

public:
void foo()
{i = 5;}

};

foo2(const myclass & num)
{
num.foo();
}

int mani ()
{
myclass num;
foo2(num);
}

изменят член i в объекте?

safocl ★★
() автор топика
Ответ на: комментарий от KivApple

так тут речь то именно о компиляторе, тоесть он сам знает, что константу менять нельзя, однако делает необходимым указывать енто дважды.

safocl ★★
() автор топика
Ответ на: комментарий от safocl

Давай так:

class myclass
{
private:
int i = 0;

public:
void foo(); // реализация в другом файле
};

foo2(const myclass & num)
{
num.foo();
}

int main ()
{
myclass num;
foo2(num);
}

Должен компилятор тебе позволить вызвать foo() или нет?

unC0Rr ★★★★★
()
Ответ на: комментарий от unC0Rr

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

safocl ★★
() автор топика
Ответ на: комментарий от safocl

Хорошо, зайдём с другой стороны. Вот тебе пример:

Foo.h

class Foo
  {
	private:
		int m_foo;
		int m_bar;

	public:
		Foo(): m_foo(0), m_bar(1) {}

		int getFoo();
  };
Foo.cpp
#include "Foo.h"

int Foo::getFoo()
  {
	m_bar = 0;
	return m_foo;
  }

Foo.cpp собран как библиотека. Как ты проверишь, что getFoo() не изменяет состояние объекта?

XMs ★★★★★
()
Последнее исправление: XMs (всего исправлений: 1)
Ответ на: комментарий от safocl

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

В общем случае, у тебя вообще нет исходников реализации. Только проприетарный бинарник и заголовочный файл с объявлениями функций/методов/etc. В таких условиях компилятор может судить о том, что именно функции делают со своими аргументами (включая this), только на основе объявлений (declarations).

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

Deleted
()
Ответ на: комментарий от XMs

Уточню, а то поймёшь неправильно: он бы это сделал, не будь аргумент константным. А так тут ошибка компиляции

XMs ★★★★★
()
Ответ на: комментарий от safocl

ты МОЖЕШЬ поменять их.

вот ентого я и не допираю кого поменять?

Представь, что компилятор видит в месте использования метода только его объявление, но не определение. Как можно узнать, что метод, вызванный у константного объекта не меняет его состояния (ведь тела метода компилятор не увидит)? Для этого метод помечается const и в месте его использования на константном объекте компилятор будет знать, что так делать можно. А в месте его определения компилятор проверит, что const метод не меняет члены класса (кроме помеченных mutable).

anonymous
()
Ответ на: комментарий от XMs

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

safocl ★★
() автор топика
Ответ на: комментарий от XMs

вот, все верно, так зачем тада еще раз указывать после скобок у функции чо const ? если и так выдаст ошибку.

safocl ★★
() автор топика
Ответ на: комментарий от anonymous

т.е. хочеш сказать, что такие уже собранные либы реализаций методов компилятор никак не может проверить на работоспособность и правильность и соответствию стандартам?

тогда как он проверит соответствие объявления реализации?

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

safocl ★★
() автор топика
Последнее исправление: safocl (всего исправлений: 2)
Ответ на: комментарий от safocl

он жеж включает текст просто когда инклудишь либу

Какой текст? В либе исходного кода больше нет, всё, она скомпилирована. И единственное, что остаётся, это смотреть на объявления в хедере. Компилятор, следующий стандарту, во время компиляции объектника сделает все проверки, и если ошибок нет — всё, вот тебе гарантия, что метод ничего не меняет

XMs ★★★★★
()
Ответ на: комментарий от safocl

Ошибку он выдаст не потому, что ты что-то меняешь, а потому, что для const-объекта дёргаешь не-const-метод

XMs ★★★★★
()
Ответ на: комментарий от safocl

собранные либы реализаций методов компилятор никак не может проверить на работоспособность и правильность и соответствию стандартам?

Именно. Для него не существует даже экспортируемых символов — он полагается на то, что раз const, то всё хорошо. Если это не так, то либа собрана не соответствующим стандарту компилятором и не может быть использована в сколь-нибудь серьёзном проекте, пока не будет пересобрана нормально.


или же все таки компилятор при изменении выдал бы ошибку?

Компилятор выдаст ошибку, но другого плана: обращение к неконстантному методу константного объекта

XMs ★★★★★
()
Ответ на: комментарий от XMs

ясно, ладно, думаю я окончательно вник, просто не рассматривал изначально такой случай, который вы описывали здесь, когда подключается уже собранная либа.
всем спс.

safocl ★★
() автор топика
Ответ на: комментарий от XMs

Потому что это логично. Const — гарантия, что объект не поменяет своё состояние.

Неверно. Что же у вас категоричность такая дырявая - ты забыл про mutable.

nebanbmenyapls
()
Ответ на: комментарий от safocl

Никакого отношения к «подключаемым либам» проблема не имеет. Подключаемые либы это лишь один из вариантов.

Ты 100% используешь колхозный мусор вида *.cpp файлов, а значит ты получаешь все эти проблемы без какой-либо либы. Везде, где есть раздельная компиляция - есть эта проблема. И она не ограничена либами.

nebanbmenyapls
()
Ответ на: комментарий от nebanbmenyapls

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

safocl ★★
() автор топика
Ответ на: комментарий от nebanbmenyapls

Mutable не стал упоминать, чтобы не усложнять. И так на целую страницу вышло

XMs ★★★★★
()
Ответ на: комментарий от safocl

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

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

Если совсем просто:

struct c {
  size_t x;
  void f();
};

Вот то, что будет иметь компилятор при раздельной компиляции. Очевидно, что никакого тела тут нет и мы никак не узнаем - изменяется ли в f() x, либо нет.

Для этого вводится константность в сигнатуру, а именно:

struct c {
  size_t x;
  void f() const;
};

Тогда мы уже знаем, что эта функция никак x не изменит, ведь компилятор гарантирует соблюдение сигнатур у объявления/определения. Таким образом по этой сигнатуре ты сможешь вызвать только:

void c::f() const {}

А,

void c::f() {}

непрокатит, т.к. у этой функции иная сигнатура. Таким образом при определении функции ты будешь обязан указать const, а раз ты указал const и тело функции есть - компилятор может его проверить, тем самым гарантируя неизменность x, соблюдая семантику константности объекта. В рамках это семантики никакие не-mutable поля структуры не могут изменяться для const-объекта.

Именно поэтому это и добавлено в стандарт. Почему компилятор не может это вывести сам - я уже рассказал выше.

nebanbmenyapls
()
Ответ на: комментарий от nebanbmenyapls

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

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

safocl ★★
() автор топика
Последнее исправление: safocl (всего исправлений: 1)
Ответ на: комментарий от safocl

Ну ты трудный!

Тебе же выше написали const существует на этапе компиляции, в машинном коде нет никаких const для X86. Как ты сам себе представляешь отслеживание изменений данных в памяти (которые происходят в процессе работы программы) без сборщика мусора?

anonymous
()
Ответ на: комментарий от safocl

Меня так удивляет, когда пациент сообщает мне о «ты не ответил», «ты не ответил». Действительно, это не ты не понял, а я не ответил. Вместо этого иди и перечитай ещё раз.

Ты вообще какой-то альтернативно одарённый, ведь тебе уже всё о ссылке рассказали. Я даже не стал это комментировать, но ты не унимаешься. f(const size_t & b) {this.x = b} - каким образом твоя константа в ссылке защитит this.x от изменения? Ты вообще понимаешь, насколько невероятную ахинею ты несёшь?

Я даже не знаю как надо упороться, чтобы не отличать две совершенно разные сущности друг от друга - ты бы хоть попытался. Не называй их одним именем, а то у тебя слишком сложно с контекстом. Пиши:

void setNumber(const int a) {this->b = a;}

nebanbmenyapls
()
Вы не можете добавлять комментарии в эту тему. Тема перемещена в архив.