LINUX.ORG.RU

Почему в константном методе невозможно вернуть указатель?

 , ,


0

2

Имеется следующий старенький код, в котором необходимо метод getTableData() сделать константным:

class TreeItem
{
 public:

    // Взятие ссылки на данные конечных записей
    RecordTableData *getTableData(void) const;
 
 private:
     RecordTableData recordsTable;
}

...

RecordTableData *TreeItem::getTableData(void) const
{
  return &recordsTable;
}

Насколько я понимаю, модификатор метода const должен следить за тем, чтобы в методе не происходило изменения объекта класса. И никакого изменения не происходит: берется адрес мембера и возвращается как указатель. Все. Что там будет происходить с переданными данными - будут они меняться по указателю, не будут - это не забота метода getTableData(). Он данные не изменил, а значит метод константный.

Но компилятор почему-то показывает ошибку:
TreeItem.cpp:632:10: error: binding reference of type 'RecordTableData' to value of type 'const RecordTableData' drops 'const' qualifier

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

★★★★★

Последнее исправление: Xintrea (всего исправлений: 2)

Почему в константном методе невозможно вернуть указатель?

Потому что в константном методе указатель this константный, а посему &(this->recordsTable) тоже константа.

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

Варианта два: 1) сменить тип возвращаемого значения на const RecordTableData*; 2) использовать const_cast (что костыль и чревато проблемами)

annulen ★★★★★
()

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

alysnix ★★★
()

Или так:

class TreeItem
{
 public:
    RecordTableData *getTableData(void) const;
 private:
    mutable RecordTableData recordsTable;
};

RecordTableData *TreeItem::getTableData(void) const
{
  return &recordsTable;
}

Или так:

class TreeItem
{
 public:
    const RecordTableData *getTableData(void) const;
 
 private:
     RecordTableData recordsTable;
};

const RecordTableData *TreeItem::getTableData(void) const
{
  return &recordsTable;
}

Константный метод сам мемберы не меняет(кроме mutable) и другим не дает.

imatveev13
()

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

const RecordTableData *TreeItem::getTableData() const {
  return &recordsTable;
}

RecordTableData *TreeItem::getTableData(){
  return &recordsTable;
}

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

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

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

alysnix ★★★
()

В общем, я пока изменил так:

RecordTableData *TreeItem::getTableData(void) const
{
    // В const-методе this является константным,
    // поэтому конструкция &recordsTable возвращает константные данные
    // Преобразование через const_cast<RecordTableData *>
    // снимает эту константность, и позволяет вернуть простой указатель
    return const_cast<RecordTableData *>( &recordsTable );
}

Все равно всю структуру надо по-хорошему переделывать.

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

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

пиши два метода и не гони пургу. два метода - это канонический такой способ.

alysnix ★★★
()
Последнее исправление: alysnix (всего исправлений: 1)

Компилятор всё правильно тебя бьёт по рукам. Перестань так делать, а не пытайся обойти. Если тебе не нужен const, то не ставь этот квалификатор. А если поставил, то изволь следовать правилам.

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

Все равно всю структуру надо по-хорошему переделывать.

Ну если переделывать, можно последовать совету отсюда:

https://isocpp.github.io/CppCoreGuidelines/CppCoreGuidelines#c131-avoid-trivial-getters-and-setters

Reason

A trivial getter or setter adds no semantic value; the data item could just as well be public.

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

это не «тривиальный» геттер. он возвращает указатель на обьект, а не значение обьекта. ваш случай не тот, что обсуждается. а мы обсуждаем констатность/неконстатность.

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

Банальное «лучше» или «еще лучше» не имеет семантической ценности; элемент данных с таким же успехом может быть общедоступным.

Да, все правильно. Этот элемент ранее и был public.

Я его спрятал, и стал выплясывать с константностью. В принципе получилось относительно правильно - все наружу уходит только как константа. В процессе правки других зависимых методов только в одном месте пришлось делать const_cast<>, но тоже в сторону константности.

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

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

YAGNI(https://ru.wikipedia.org/wiki/YAGNI) не рекомендует методов кот. могут пригодиться.

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

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

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

обсуждение про неправильность совета

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

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

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

а простые классы пишутся как struct, и им сеттеры/геттеры обычно не пишут.

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

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

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

в плюсовой культуре

CppCoreGuidelines предостерегают от плохих практик закрепившиеся в культуре.

https://github.com/isocpp/CppCoreGuidelines/issues/776

«standard practice» does not mean you don’t have a framework problem. A lot of the rules in the Core Guidelines go against things that someone somewhere considers «standard practice».

One of the goals of the Core Guidelines is help C++ programmers write good C++, adopt solid and robust practices. We feel that trivial setters and getters are to be avoided. This is based on decades on experience, writing code, maintaining code written by others, reviewing code. It isn’t lecturing anybody who disagrees with those insights.

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

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

а взятие адреса чревато многими проблемами. например если его передадут куда-то или запомнят, а объект переместится или удалится.

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

а взятие адреса чревато многими проблемами. например если его передадут куда-то или запомнят, а объект переместится или удалится.

Опыт говорит что это происходит редко, а лишний код остается навсегда.

Проблема убежавших указателей никак не связана с классами и сетерам/геттерами. За указателями надо следить и для этого есть много инструментов.

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

Да нет там ответов.

Implementation can change - раз, разница между debug и release — два. Никто по сути ничего внятного не возразил, один лепет какой-то.

В первом случае нужно будет менять API и ломать обратную совместимость. Это хороший опыт, не спорю — из-за бездумного следования правилу, потому что так сказали «ох какие авторитетные люди» на пустом месте создать себе проблемы на будущее — любимое занятие программеров.

Во втором случае — надрывая пятую точку искать другие способы отладки там, где можно этого не делать. Тоже хороший опыт.

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

Опыт говорит что это происходит редко, а лишний код остается навсегда.

это не лишний ващета код. компилятор будет инлайнить тривиальные сеттеры/геттеры, если конечно видит код их тела. то есть они в хидере написаны.

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

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

Вы ищете логичное объяснение. Его там нет. Есть про опыт:

One of the goals of the Core Guidelines is help C++ programmers write good C++, adopt solid and robust practices. We feel that trivial setters and getters are to be avoided. This is based on decades on experience, writing code, maintaining code written by others, reviewing code. It isn’t lecturing anybody who disagrees with those insights.

Вам пытаются сообщить что практика тривиальных сеттеров/геттеров, кот. кажется логичной, принесет вам проблемы. Это выяснили опытным путем.

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

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

их десятилетия нерелевантны. они просто живут неверную жизнь.

и вообще это флуд. к вопросу константности это не имеет отношения, и даже противоречит самому принципу константности.

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

Аргумент против геттеров/сеттеров есть: проблемы кот. они решают возникают редко и их можно решить другими способами. Лишний код остается навсегда.

Все аргументы за сеттеры/геттеры основаны на «что то может случиться». Хочется в ответ спросить - Почему ходишь без каски?

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

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

Согласен. Меня пока успокаивает тот факт, что этот единственный const_cast<> написан в сторону добавления константности.

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

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

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

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

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

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

«Мы тут чувствуем, что так лучше, это нам подсказывает наш опыт, но объяснить и формализовать это утверждение не можем; если у вас другой опыт, то он не правильный и не значимый, даже если вы можете его обосновать. End of discussion.».

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

решить проблему неверного использования поля

можно решить верным использованием поля.

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

с какой целью запрещать C++ программисту манипулировать адресами?

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

Утверждение: «проблемы кот. решают геттеры/сеттеры возникают редко» невозможно доказать логически. Это результат опыта. Вам об этом опыте пытаются сообщить, вы не хотите слушать.

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

можно решить верным использованием поля.

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

с какой целью запрещать C++ программисту манипулировать адресами?

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

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

Опыт, который нельзя формализовать — это напрасный опыт, потому что он вообще ни о чём не говорит. Воспринимать такой «опыт» как догму довольно странно. Те проблемы, которые реально решают геттеры/сеттеры были перечислены и обоснованы. В ответ только: «да вы что, не понимаете? У нас опыт! Но мы вам о нём не расскажем, просто поверьте на слово.».

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

можно решить верным использованием поля.

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

Правильное использование поля никак не связано с его квалификаторами. Только с алгоритмом использования. Квалификаторы намекают как автор класса задумал использование поля, но эта информация не полна. Если автор считает что поле никогда не должно использоваться или меняться снаружи то квалификаторы нужны. Однако такое принуждение к задуманной автором семантике класса ужасно мешает при попытке переиспользовать его класс в другом коде. Имею с этой проблемой дело каждый день, и вспоминаю добрым словом авторов кот. пишут лишний код.

с какой целью запрещать C++ программисту манипулировать адресами?

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

«Увлекаться» и «запрещать» несколько разной степени обязательности. А уметь манипулировать указателями C++ программист должен.

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

Дык в отладчике можно поставить прерывание на запись в переменную

Так я это и имел ввиду под «надрывая пятую точку искать другие способы отладки». Я бы не сказал, что это проще, чем прописать лог в сеттере, который будет компилироваться только в отладке. Опять же, на сеттер тоже можно поставить брейк.

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

Правильное использование поля никак не связано с его квалификаторами.

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

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

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

А в релизе компилятор всё равно будет инлайнить этот сеттер setX(y) до x=y

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

Опыт, который нельзя формализовать — это напрасный опыт, потому что он вообще ни о чём не говорит

С этого места пож. подробнее. Можете привести пример формализации опыта? Что вы вообще понимаете под формализацией опыта?

У нас опыт! Но мы вам о нём не расскажем, просто поверьте на слово.

Очень даже расскажем.Весь интернет завален примерами.

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

Однако такое принуждение к задуманной автором семантике класса ужасно мешает при попытке переиспользовать его класс в другом коде

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

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

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

квалификаторы тут - рестрикции, они же ограничения, на использование поля

Складывается впечатление что вы считаете ограничения добром по умолчанию.

давая по рукам квир-диссидентам

Странный довод в техническом обсуждении.

imatveev13
()