mutable c что это
Ключевое слово «mutable» в C++
Ключевое слово mutable относится к малоизвестным уголкам языка С++. В то же время оно может быть очень полезным, или даже необходимым в случае, если вы хотите строго придерживаться const-корректности вашего кода или писать лямбда-функции, способные изменять своё состояние.
Пару дней назад Eric Smolikowski написал в своём твиттере:
«Я часто спрашиваю программистов на собеседовании насколько хорошо (по 10-бальной шкале) они знают С++. Обычно они отвечают 8 или 9. И тогда я спрашиваю что такое „mutable“. Они не знают. :)»
Впечатления от таких вопросов и ответов у меня двоякие. С одной стороны, задавать подобные вопросы на собеседовании — дело бесполезное, это почти ничего не говорит о способностях интервьюируемого. Но, с другой стороны, ключевое слово mutable незаслуженно забыто многими программистами, а ведь оно может быть очень полезным в некоторых сценариях.
Const-корректность: семантическая константность против синтаксической константности
Когда мы пытаемся писать код, корректный с точки зрения использования понятия константности, то столкнёмся с тем, что семантическая неизменность не эквивалентна синтаксической неизменности. Другими словами, нам может понадобиться изменить состояние объекта (если этого требуют детали реализации), сохранив при этом видимое извне состояние объекта константным.
Изменение внутреннего состояния может требоваться по каким-то глубоко техническим причинам и это не должно быть заметно для внешних клиентов нашего класса. Но выбор у нас не большой — если мы используем ключевое слово const при объявлении метода, то компилятор не позволит нам изменить объект этого класса, даже если эти изменения никто вне класса и не заметит.
Кешированные данные
Хорошим примером может быть кеширование данных. Давайте посмотрим на вот этот класс полигона:
Давайте предположим, что geometry::calculateArea — это очень ресурсозатратная функция, которую мы не хотим вызывать каждый раз при вызове метода area(). Мы можем рассчитывать новую площадь при изменении полигона, но в некоторых сценариях это может быть настолько же (или даже больше) ресурсозатратно. Хорошим решением в данной ситуации может быть вычисление площади только тогда, когда это необходимо, с кеширование результата и очисткой кеша в случае изменения полигона.
Но, эй, погодите, не так быстро! Компилятор не даст вам провернуть подобный фокус, ведь метод area() помечен константным, а мы зачем-то пытаемся в нём изменять свойство cachedArea. Убрать const из объявления метода? Но тогда нас не поймут клиенты данного класса. Ведь area() — это простой геттер, данная функция точно не должна менять ничего в классе. Так почему же в её объявлении нет const?
Мьютексы
Ещё один пример это потокобезопасность с использованием мьютексов. Контейнер вершин в примере выше не потокобезопасен. Таким образом, в многопоточном приложении, где разные потоки будут совместно использовать данные одних и тех же полигонов, нам необходимо обеспечить безопасность доступа к этим данным:
В данном случае компилятор снова начнёт жаловаться на метод area(), который бодро обещает быть константным, но сам (вот ведь негодяй!) пытается выполнить операцию mutex::lock(), которая меняет состояние мьютекса. То есть — мы не можем залочить константный мьютекс.
Получается, что мы снова не можем сделать метод area() константным и будем вынуждены либо отказаться от потокобезопасности, либо вводить в заблуждение клиентов нашего класса, избавляясь от const в объявлении метода. Из-за технических деталей реализации, которые не имеют совершенно никакого отношения к видимому извне состоянию объекта, нам приходится либо отказываться от части функционала, либо вводить в заблуждение пользователей класса.
Ключевое слово «mutable» спешит на помощь
Ключевое слово mutable существует в стандарте языка С++ именно для решения данного класса проблем. Его можно добавить к переменным членам класса для указания того, что данная переменная может изменяться даже в константном контексте. С использованием mutable решение обоих вышеуказанных примеров будет выглядеть вот так:
Изменяемые лямбда-функции
Есть и ещё один вариант применения ключевого слова mutable и он связан с сохранением состояния в лямбда-функциях. Обычно оператор вызова функции замыкания является константным. Другими словами — лямбда не может модифицировать переменные, захваченные по значению:
Но ключевое слово mutable может быть применено ко всей лямбда-функции, что сделает все её переменные изменяемыми:
Следует заметить, что в отличии от mutable-переменных в объявлении класса, мутабельные лямбда-функции должны использоваться относительно редко и очень аккуратно. Сохранение состояния между вызовами лямбда-функции может быть опасным и контринтуитивным.
О вреде изменяемых значимых типов
Большинство программистов, которых нелегкая судьба свела с платформной.Net знают о существовании значимых типов (value types) и ссылочных типов (reference types). И довольно многие из них прекрасно знают, что помимо названия, эти типы имеют и другие различия, такие как расположение объектов оных типов в памяти, а также в семантике.
Что касается первого различия (о котором стоит упомянуть как минимум ради полноты изложения), то оно заключается в том, что экземпляры ссылочных типов всегда располагаются в управляемой куче, в то время как экземпляры значимых типов по умолчанию располагаются в стеке, но могут мигрировать в управляемую кучу вследствие упаковки, будучи членами ссылочных типов, а также при использовании их в хитрых экзотических конструкциях языка C#, типа замыканий (*).
Хотя это отличие является очень важным и именно благодаря нему значимые типы существуют и используются, у этой пары типов есть еще одно, не менее важное семантическое различие. Значимые типы, как подсказывает название, являются значениями, которое копируется каждый раз при передаче в функцию или при возвращении из нее. А поскольку при копировании, как, опять же, подсказывает название, передается и возвращается не исходный вариант, а копия, то все попытки изменений приведут к изменению копии, а не исходного экземпляра.
В теории, последнее заявление кажется настолько простым и очевидным, что оно кажется недостойным внимания, однако в языке C# существует ряд моментов, когда копирование происходит настолько неявно, что преводит к копированию совершенно не того экземпляра, о котором думает разработчик, что приводит его (разработчика) в легкое замешательство.
Давайте рассмотрим некоторые из таких примеров.
1. Изменяемый значимый тип в виде свойства объекта
Давайте начнем с относительно простого примера, в котором копирование происходит достаточно явно. Предположим у нас есть некоторый изменяемый значимый тип (который, кстати, нам пригодится не только для этого, но и для всех последующих примеров) под названием Mutable и некоторый класс A, который содержит свойство указанного типа:
Пока, вроде бы, ничего интересного, но давайте посмотрим на следующий пример:
Хотя компилятор понимает семантику оператора ++, в общем случае он понятия не имеет о том, что делает конкретная функция с текущим объектом, в частности, изменяет ли она его или нет. И хотя мы не можем вызвать оператор ++ свойства Y в предыдущем фрагменте кода, мы спокойно сможем вызвать метод IncrementX свойства X:
Хотя предыдущий код ведет себя некорректно, заметить ошибку невооруженным взглядом не всегда просто. Каждый раз при обращении к свойству Mutable класса создается новая копия, для которой и вызывается метод IncrementX, но поскольку изменение копии никакого отношения к изменению исходного объекта не имеет, то и вывод на консоль, при выполнении предыдущего фрагмента кода будет соответствующий:
Исходное значение Mutable.X: 5
Mutable.X после вызова IncrementX(): 5
«Хм… ничего сверхъестественного», скажите вы и будете правы… до тех пор, пока мы не рассмотрим другие, более интересные случаи.
2. Изменяемые значимые типы и модификатор readonly
Давайте рассмотрим класс B, который в качестве readonly поля содержит нашу изменяемую структуру Mutable:
class B
<
public readonly Mutable M = new Mutable(x: 5, y: 5);
>
Опять-таки, это не rocket science, а самый простой класс, единственным недостатком которого является использование открытого поля. Но поскольку открытость этого поля обусловлена простой примера и удобством, а не ошибками дизайна, то обращать внимание на эту мелочь не стоит. Вместо этого, стоит обратить внимание на простой пример использования этого класса и на получаемые результаты.
Вроде бы M – это не свойство, которое будет копироваться каждый раз при его возвращении, так что ответ 8 кажется вполне логичным. Однако, компилятор (и спецификация языка C#, кстати, тоже) с нами не согласятся и, в результате выполнения этого кода, M.X все еще будет равен 5:
Исходное значение M.X: 5
M.X после трех вызовов IncrementX(): 5
Все дело здесь в том, что согласно спецификации, при обращении к полю только для чтения вне конструктора, генерируется временная переменная, для которой и вызывается метод IncrementX. По сути, предыдущий фрагмент кода компилятором переписывается таким образом:
(Да, если вы уберете модификатор readonly, то вы получите ожидаемый результат; после трех вызовов метода IncrementX значение свойства X переменной M будет равно 8.)
3. Массивы и списки
Поскольку индексатор списка является обычным свойством, то его поведение не отличается от того, что описано в первом разделе: каждый раз, обращаясь к элементу списка, мы будем получать не исходный элемент, а его копию.
lm[0].Y++; // Ошибка компиляции
lm[0].IncrementX(); // ведет к изменению временной переменной
Теперь давайте попробуем проделать ту же самую операцию с массивом:
В этом случае большинство разработчиков будут предполагать, что индексатор массива ведет себя аналогичным образом, возвращая копию элемента, который затем и изменяется в нашем коде. И поскольку язык C# не поддерживает такую возможность, как возвращение «управляемых указателей» (managed pointers) из функции, то других вариантов, вроде бы и нет. Ведь все, что мы можем, так это создавать синонимы нашей переменной (alias) и передать ее в другую функцию с помощью ключевых слов ref или out, но мы не можем написать функцию, возвращающую ссылку на одно из полей объекта.
Но хотя язык C# и не поддерживает возвращение управляемых ссылок в общем случае, существует особая оптимизация в виде специальной инструкции IL-кода, которая позволяет получить не просто копию элемента массива, а ссылку на него (для любознательных, эта инструкция называется ldelema). Благодаря этой возможности, предыдущий фрагмент не только полностью корректен (включая строку am[0].Y++;), но и позволяет изменить непосредственно элементы массива, а не их копии. И если вы запустите предыдущий фрагмент кода, то увидите, что он компилируется, запускается, и напрямую изменяет нулевой объект массива.
Исходные значения X: 5, Y: 5
Новые значения X:6, Y:6
Mutable[] am = new Mutable[] < new Mutable(x: 5, y: 5) >;
IList lst = am;
lst[0].Y++; // Ошибка компиляции
lst[0].IncrementX(); // изменение временной переменной
4. И зачем мне все это?
Классическим примером изменяемого значимого типа является структура Point, а также енумераторы, например ListEnumerator. И если в первом случае отпилить себе ногу весьма сложно, то во втором случае – будь здоров:
(Скопируйте этот код в LINQPad или в метод Main и запустите.)
Заключение
Говорить категорично о том, что изменяемые значимые типы являются полным злом точно также неверно, как и говорить о всеобъемлющем зле оператора goto. Известно, что использование оператора goto программистом напрямую в крупной промышленной системе может привести к сложному для понимания и сопровождения коду, к скрытым ошибкам и головной боли при поиске ошибок. По этой же причине нужно остерегаться и изменяемых значимых типов: если вы умеете их готовить, то аккуратное их применение может быть неплохой оптимизацией производительности. Но эта эффективность вполне может вам аукнуться позднее, когда за дело возьмется ваш сосед, который еще не выучил спецификацию языка C# на зубок и все еще не знает, что использование конструкции using со значимыми типами приводит к очистке копии (***).
Использование значимых типов уже является оптимизацией, поэтому вам обязательно нужно доказать, что их использование того стоит и вы получите повышение производительности. Использование же изменяемых значимых типов является оптимизацией в квадрате (ведь вы экономите на копии при их изменении), поэтому прежде делать свои значимые типы изменяемыми стоит подумать не n раз, а n раз в квадрате.
(*) Замыкание – это не такой уж страшный зверь, как может показаться из замысловатого названия. И если вдруг, по какой-то причине вы не уверены в своих знаниях по этому поводу, то этот как раз отличный повод это исправить: “Замыкания в языке C#”.
(***) Это тонкий намек на одну из статей Эрика Липперта (который считает изменяемые значимые типы самым большим вселенским злом), в которой он показывает «не совсем очевидное» поведение при использовании изменяемых значимых типов, реализующих интерфейс IDisposable: To box or not to box, that is a question.
Использование volatile, extern, register, auto, mutable в C++ (обзор)
В языке C++ есть квалификаторы и спецификаторы, которые в связи с их не очень частым использованием могут вызвать замешательство у неискушённых и начинающих. В данном коротком очерке я собираюсь пролить свет на данную тему.
Начнём с квалификатора volatile
Если подходить формально то квалификатор volatile информирует компилятор что переменная может быть изменена не явным способом т.е. без явного использовать оператора присвоения. И что же это означает на практике? Дело в том, что как правило компиляторы автоматически применяют оптимизацию, предполагая что значение переменной остаётся постоянным, если оно не указано с левой стороны от оператора присваивания. Т.е. если переменная не меняется, то и нет нужды проверять её при каждом обращении.
В данном случае значение переменной exit будет прочитано только один раз. На первый взгляд в этом не ничего страшного. Однако, если содержание переменно exit может изменять не только код текущего потока, но допустим другой поток (опустим сейчас вопросы атомарности операции) или вообще некое внешние устройство, то мы можем получить бесконечный цикл коим он по нашей задумке быть не должен.
Но если мы перед объявлением переменной поставим квалификатор volatile, то переменная будет считываться при каждой проверке.
Приведу более жизненный пример. Предположим есть необходимость работы с внешнем устройством через некоторый порт. Нам нужно записать в порт последовательность из трёх нулей.
После оптимизации вместо трёх нулей будет записан только один. Более того после оптимизации прочитать что либо из порта будет не реально т.к. изменения переменной будут происходить во внешней среде.
Герберт Шилдт приводит такой пример: Например, адрес глобальной переменной можно передать таймеру операционной системы и использовать его для отсчёта времени. В этом случае содержимое переменной изменяется без явного выполнения какого-либо оператора присвоения.
Чисто теоретически если быть стопроцентно уверенным в атомарности операции, то можно использовать квалификатор volatile для взаимодействие переменной между потоками без использования дополнительных объектов синхронизации таких как мьютексы, но поскольку такую уверенность может дать разве что char, то применять volatile в этих целях будет не корректным.
Применять volatile в дополнение к мьютексам нет необходимости, хотя на эту тему сломано много копий, но примеров реальных компиляторов и ОС, где мьютексов мало, мне не встречалось. Кстати, функции-члены тоже могут объявляться со словом volatile:
В данном случае внутри foo() указатель this так же имеет атрибут volatile.
Спецификатор extern
В языках с\с++ существуют внутренние связи, внешние связи и отсутствие связей. Глобальные переменные имеют внешние связи и это позволяет получить доступ к ним из любой части программы. Если к глобальным переменным добавить спецификатор static, то глобальные переменные утратят внешние связи и будут иметь только внутренние связи, т.е. будут доступны только внутри файла, в котором они были описаны. Локальные переменные не имеют связей и поэтому доступны только внутри блока где они были описаны.
Спецификатор extern указывает, что переменная обладает внешними связями. Дело в том, что надо различать определение и объявление. Объявление указывает имя объекта и его тип, то где как определение выделяет под объект память. Таким образом можно сделать несколько объявлений объекта и только одно определение. В большинстве случаев, определение и объявление совпадают. Спецификатор extern позволяет объявить переменную без её определения т.е без выделения памяти. Используя спецификатор extern можно путём объявления обратиться к переменной, определённой в другом месте. К примеру, можно определить все глобальные переменные в одном файле, а в других файлах получать к ним доступ через объявление со спецификатором extern.
Спецификатор register
Изначально это спецификатор применялся только к переменным типа char и int, но теперь его можно применять к переменным любого типа. Данный спецификатор указывает компилятору хранить значение переменной не в памяти, а в регистре процессора. Иной трактовкой спецификатора register служит подсказка компилятору, что данный объект используется очень интенсивно. Разумеется в регистрах смогут поместиться только данные весьма ограниченного объёма, такие как int и char, а боле крупные объекты в регистры не поместятся, но получат более высокой приоритет обработки. Надо учитывать, что register это рекомендация такая же как inline и компилятор может просто игнорировать спецификатор register и обрабатывать переменную как обычно.
Так же замечу, что спецификатор register можно применять только к локальным переменным и формальным параметрам.
Компилятор автоматически преобразует лишние регистровые переменные в обычные, поэтому нет смысла в подсчёте количества регистровых переменных что обеспечивает машино-независимость.
Ключевое слово auto
Ключевое слово mutable
Иногда есть необходимость изменить некий объект внутри класса, гарантируя неприкосновенность остальных элементов. Неприкосновенность можно гарантировать при помощи const, однако const запрещает изменение всего.
Помочь в данном случае может определение переменной а с ключевым словом mutable. Внесём исправление в приведённый чуть выше пример:
————
Александр Бабашов (tellurian)
Если Вам понравилась статья, проголосуйте за нее
Голосов: 46 Голосовать
Functional C#: Immutability
Это первая статья из небольшой серии, посвященной программированию на C# в функциональном стиле. Серия не про LINQ, как можно было бы подумать, а про более фундаментальные вещи. Навеяно F#-ом.
Immutability (неизменяемость)
Наибольшая проблема в мире enterprise разработки — это борьба со сложнотью. Читаемость кода — это пожалуй первое чего мы должны стараться достичь при написании любого более-менее сложного проекта. Без этого наша способность понимать код и принимать на основе этого разумные решения значительно ухудшается.
Помогают ли нам изменяемые объекты при чтении кода? Давайте рассмотрим пример:
Изменился ли queryObject к моменту поиска кастомеров во второй раз? Может быть, да. А может, и нет. Это зависит от того, был ли этот объект изменен методом AdjustSearchCriteria. Чтобы выяснить это, нам необходимо заглянуть внутрь этого метода, его сигнатура не дает нам достаточной информации.
Сравните это со следующим кодом:
В этом примере ясно, что AdjustSearchCriteria создает новый объект критерия и использует его в дальнейшем для нового поиска.
Так в чем проблема с изменяемыми структурами данных?
Как создавать неизменямые типы
В будущих версиях C# возможно появится ключевое слово immutable. С его помощью можно будет понимать является ли тип неизменямым просто глядя на его сигнатуру. Пока же нам приходится пользоваться тем, что есть.
Если вы имеете сравнительно простой класс, рассмотрите возможность сделать его неизменямым. Этот гайд-лайн коррелирует с понятием Value Objects.
Возьмем для примера класс ProductPile, описывающий какое-то количество продуктов на продажу:
Чтобы сделать его неизменяемым, мы можем пометить его свойства как read-only и добавить конструктор:
Теперь предположим, что вам необходимо уменьшать свойство Amount на единицу каждый раз когда вы продаете один из продуктов. Вместо того, чтобы изменять имеющийся объект, мы может создавать новый на основе имеющегося:
Ограничения
Конечно, каждая полезная практика имеет свою цену. В то время как небольшие классы пользуются преимуществами неизменяемости в полной мере, такой подход не всегда применим в случае с большими типами.
В первою очередь, неизменяемость несет в себе потенциальные проблемы с производительностью. Если объект довольно большой, необходимость создавать его копию при каждом изменении может стать проблемой.
Хорошим примером тут будут неизменяемые коллекции. Авторы учли потенциальные проблемы с производительностью и добавили специальный класс Builder, который позволяет изменять состояние коллекций. После того, как коллекция приведена к необходимому состоянию, ее можно финализировать, конвертировав в неизменяемую:
Заключение
В большинстве случаев, неизменяемые типы (особенно если они довольно просты) делают код лучше.
Mutable c что это
Здравствуйте, vvaizh, Вы писали:
V>Просветите неуча..
V>в первый раз вижу в исходниках..
V>что такое не знаю..
V>и вообще кроме как gcc что-нибудь это понимает?
V>а может это и не ключевое слово вообще, а в какой нибудь либе определяется?
V>man не помогает..
| От: | Аноним |
Дата: | 09.04.03 14:09 | |
Оценка: | 6 (1) +2 |
Здравствуйте, vvaizh, Вы писали:
V>Просветите неуча..
V>в первый раз вижу в исходниках..
V>что такое не знаю..
V>и вообще кроме как gcc что-нибудь это понимает?
V>а может это и не ключевое слово вообще, а в какой нибудь либе определяется?
V>man не помогает..
mutable — значит «не const ни при каких обстоятельствах»
Т.е. если есть такое объявление:
То такой код компилироваться не будет. Т.к. функция Lock и Unlock изменяют объект this, а он передан в функцию SomeFunct как const.
А если определить переменную m_bLock как mutable, то все будет ништяк.
class CSomeResource
<
mutable bool m_bLock;
public:
Lock()< m_bLock = true; >
Unlock()< m_bLock = false; >
void Read( LPCTSTR szBuffer, DWORD dwSize );
>;
Компилятор в этом случае похоже не следит за изменениями в переменной m_bLock, если весь объект this константный.
Заранее прошу прощения, если в чем-то здесь ошибся, но вроде как правильно.
| От: | Коваленко Дмитрий | http://www.ibprovider.com |
Дата: | 09.04.03 14:16 | ||
Оценка: |
Здравствуйте, Bell, Вы писали:
Интересно. До сих пор я, по-простоте душевной, думал, что mutable связана с константными методами.
| От: | Lorenzo_LAMAS |
Дата: | 09.04.03 14:17 | |
Оценка: | +1 |
| От: | Коваленко Дмитрий | http://www.ibprovider.com |
Дата: | 09.04.03 14:20 | ||
Оценка: | +2 |
Здравствуйте, Amor, Вы писали:
A>То такой код компилироваться не будет. Т.к. функция Lock и Unlock изменяют объект this, а он передан в функцию SomeFunct как const.
Он не будет компилироваться (не должен, по крайней мере) потому что Lock, Unlock не константные методы.
| От: | Amor |
Дата: | 09.04.03 14:26 | |
Оценка: |
Здравствуйте, Коваленко Дмитрий, Вы писали:
КД>Здравствуйте, Amor, Вы писали:
A>То такой код компилироваться не будет. Т.к. функция Lock и Unlock изменяют объект this, а он передан в функцию SomeFunct как const.
КД>Он не будет компилироваться (не должен, по крайней мере) потому что Lock, Unlock не константные методы.
Т.е. не получится вызвать неконстантную функцию, но которая не изменяет объект? Т.е. это будет компилироваться?
помоему это не так.
| От: | Коваленко Дмитрий | http://www.ibprovider.com |
Дата: | 09.04.03 14:30 | ||
Оценка: |
Здравствуйте, Amor, Вы писали:
КД>Он не будет компилироваться (не должен, по крайней мере) потому что Lock, Unlock не константные методы.
A>Т.е. не получится вызвать неконстантную функцию, но которая не изменяет объект? Т.е. это будет компилироваться?
A>помоему это не так.
Это ты компилятору скажи. BCB3 выдал warning.
PS. какой-то песец с раскраской сообщений
| От: | Михаил Можаев | www.mozhay.chat.ru |
Дата: | 09.04.03 14:35 | ||
Оценка: | 1 (1) |
Здравствуйте, Коваленко Дмитрий, Вы писали:
КД>Интересно. До сих пор я, по-простоте душевной, думал, что mutable связана с константными методами.
The mutable specifier on a class data member nullifies a const specifier applied to the containing class object and permits modification of the mutable class member even though the rest of the object is const. (7.1.5.1).
И так просто, вдобавок:
The mutable specifier can be applied only to names of class data members (9.2) and cannot be applied to names declared const or static, and cannot be applied to reference members. [Example:
—end example]
| От: | Дмитрий Наумов |
Дата: | 09.04.03 14:35 | |
Оценка: |
Здравствуйте, Коваленко Дмитрий, Вы писали:
КД>Это ты компилятору скажи. BCB3 выдал warning.
BCB3 совсем не авторитет, а даже наоборот.
| От: | Amor |
Дата: | 09.04.03 14:36 | |
Оценка: |
Здравствуйте, Коваленко Дмитрий, Вы писали:
КД>Здравствуйте, Amor, Вы писали:
КД>Он не будет компилироваться (не должен, по крайней мере) потому что Lock, Unlock не константные методы.
A>Т.е. не получится вызвать неконстантную функцию, но которая не изменяет объект? Т.е. это будет компилироваться?
A>помоему это не так.
КД>Это ты компилятору скажи. BCB3 выдал warning.
КД>PS. какой-то песец с раскраской сообщений
стопудодво, ты прав!
| От: | centurn |
Дата: | 09.04.03 14:37 | |
Оценка: |
Здравствуйте, Amor, Вы писали:
КД>Он не будет компилироваться (не должен, по крайней мере) потому что Lock, Unlock не константные методы.
A>Т.е. не получится вызвать неконстантную функцию, но которая не изменяет объект? Т.е. это будет компилироваться?
.
A>помоему это не так.
Это не будет компилироваться, т.к. ты, не объявив метод константным, как бы сказал, что он может менять объект. А раз так, то его (метод) уже нельзя вызвать для константного объекта. И что там внутри делается, компилятора тогда уже не интересует. Я и не представляю, как можно было иначе организивать.
| От: | Bell |
Дата: | 09.04.03 14:39 | |
Оценка: |
Здравствуйте, Коваленко Дмитрий, Вы писали:
.
КД>Это ты компилятору скажи. BCB3 выдал warning.
Что-то больно мягко Comeau и VC6 послали.
КД>PS. какой-то песец с раскраской сообщений
Ну, аж в глазках рябит
| От: | Коваленко Дмитрий | http://www.ibprovider.com |
Дата: | 09.04.03 14:43 | ||
Оценка: |
Здравствуйте, Дмитрий Наумов, Вы писали:
ДН>Здравствуйте, Коваленко Дмитрий, Вы писали:
КД>Это ты компилятору скажи. BCB3 выдал warning.
ДН>BCB3 совсем не авторитет, а даже наоборот.
Да ладно
Хоть он и 98 года, но mutable понимает
| От: | Анoним |
Дата: | 09.04.03 14:53 | |
Оценка: |
Здравствуйте, Коваленко Дмитрий, Вы писали:
КД>Это ты компилятору скажи. BCB3 выдал warning.
ДН>BCB3 совсем не авторитет, а даже наоборот.
КД>Да ладно
КД>Хоть он и 98 года, но mutable понимает
В этом вопросе — точно не авторитет. Билдер очень попустительски относится к изменению констант. И у него плохо обстоят дела с константностью в шаблонах.
| От: | Анoним |
Дата: | 09.04.03 14:55 | |
Оценка: |
Здравствуйте, Михаил Можаев, Вы писали:
ММ>—end example][/q]
В связи с этим хотелось бы особо отметить важный момент: в отличие от const, mutable не является частью типа, и положение слова mutable — перед объявлением (т. е. нельзя написать: const int* mutable p;).
| От: | Коваленко Дмитрий | http://www.ibprovider.com |
Дата: | 09.04.03 15:16 | ||
Оценка: |
| От: | Анoним |
Дата: | 09.04.03 15:19 | |
Оценка: |
Здравствуйте, Коваленко Дмитрий, Вы писали:
КД>Хоть он и 98 года, но mutable понимает
А>В этом вопросе — точно не авторитет. Билдер очень попустительски относится к изменению констант. И у него плохо обстоят дела с константностью в шаблонах.
КД>В смысле? Просветите на примере
Увы, с примером будет туго. Я на нем уже больше года не пишу — сменил место работы. Но проблему помню.
| От: | Коваленко Дмитрий | http://www.ibprovider.com |
Дата: | 09.04.03 15:26 | ||
Оценка: |
Здравствуйте, Анoним, Вы писали:
А>Здравствуйте, Коваленко Дмитрий, Вы писали:
КД>Хоть он и 98 года, но mutable понимает
А>В этом вопросе — точно не авторитет. Билдер очень попустительски относится к изменению констант. И у него плохо обстоят дела с константностью в шаблонах.
КД>В смысле? Просветите на примере
А>Увы, с примером будет туго. Я на нем уже больше года не пишу — сменил место работы. Но проблему помню.
Ну тогда бы и не мутил воду . У BCB3 есть гораздо более страшные глюки, чем хреновое состояние дел с константностью в шаблонах
.