пятница, октября 24, 2008

Странные примеры использования const

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

А вот несколько необычные примеры применения const.

1. void MyFunction ( const int var);
2. const int MyFunction();
3. int const MyFunction();

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

Я свое мнение напишу чуть позже.

Updated
Как и обещала, мое мнение:
1. Нельзя будет поменять переменную var внутри функции. Я не знаю, возможно в чьих-нибудь стандартах кодирования заставляют так делать, но вообще это не принято.

2 и 3, как многие уже заметили, эквивалентны. И бесполезны. Обратите внимание, что возвращаемое значение const int. Если вы возвращаете класс, то смысл в этом будет.
Нет, можно придумать какие-нибудь странные использования этих выражений и даже заставить некоторые компиляторы их проглотить. Но вот Comeau на такое скажет

"ComeauTest.c", line 2: warning: type qualifier on return type is meaningless
const int MyFunction()

И будет прав. Бессмысленны и const, и volatile. Я как-то случайно наткнулась на информацию, что когда-то в gcc было придумано некое расширение и volatile void имело какой-то смысл. Но это было давно и ныне deprecated.

Это к вопросу о применении. А вот вопрос "что это может означать?" - интереснее. Для меня любой из приведенных примеров кода - повод для беспокойства.
Например, вот это:
int const MyFunction()

Это скорее всего кто-то перепутал с
int MyFunction() const


В-общем, это хороший повод пройтись по коммитам, найти автора и спросить "чувак, что ты хотел этим сказать?"

30 коммент.:

Dmitry Shintyakov комментирует...

1) - Вполне понятно и потенциально используемо. Фактически это защищает аргумент x внутри тела функции от изменения.
Т.е. нельзя будет написать
void f(cont int x){
x=1;
}
Хотя большой пользы от этого нет, разве что защита от случайного = вместо ==.

А вот 2) и 3) - на мой взгляд бессмыслица. Во-первых, они абсолютно эквивалентны. Во-вторых, функционально не отличаются от обчного int MyFunction().

По крайней мере, если я правильно понимаю С++.

Анонимный комментирует...

void MyFunction ( const int var);

Использую чтобы подстраховать себя от «неожиданного» изменения var в теле MyFunction. Параметр функции — точка; и он не должен менятся в процессе выполнения функции. Чтобы не получилось вначале он показывает «количество попугаев в клетке», а начиная с середины тела функции «количество красных попугаев»

Анонимный комментирует...

наверно

2) const int MyFunction();
возвращает константу int

а
3) int const MyFunction();
означает что MyFunction являектся функцией-константой, следовательно она не может менять значения объектам (кроме mutable)

Андрей Валяев комментирует...

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

Что касается второго и третьего,
5qmwpzuarnkntow2l.akvn.szoa2, насчет третьего ты заблуждаешься ИМХО... const int и int const - абсолютно идентичны.

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

const int foo();
const int &var = foo();

Хотя с таким же успехом константная ссылка может указывать на неконстантный объект. Но зато никто не сможет сделать неконстантную ссылку на константный результат! :)

int &var = foo(); // error!

feech комментирует...

ихмо конструкции используются так

1. f(const arg1&)
- в варианте из поста значение, даже при изменении внутри функции, не измениться

2. const res operator+()
- вот уж действительно забавно что-то присвоить результату какой-то операции

3. это я не знаю - просвети пожалуйста

Анонимный комментирует...

inline void f(cont int x)

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

morhveus комментирует...

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

class someObj{
const int SomeVal(); //для чтения
int & SomeOtherVal(); //для записи
};
...
someObj.SomeOtherVal() = 10;
someObj.SomeVal() = 20;

Kirill V. Lyadvinsky комментирует...

Тут было описано — http://malistov.blogspot.com/2008/08/const.html
Там автор приводит пример, когда невозможно обойтись без const.

Unknown комментирует...

Кажись все эти варианты Майерс в Эфективном С++ описывает.
1. const иммеет значение только в случае inline функции
2. const int то есть возврат константного значения из функции необходим, чтобы защитить присвоение значения результату функции, т.е.:

MyFunction() = 10;

3. Соглашусь с 5qMWPZUArNKNTOw2L.AkVN.Szoa2

Naishin комментирует...

А еще можно написать
int foo() const {}
:)

gp комментирует...

1) Как многие уже сказали, просто перестраховка для кода тела функции

2) и 3) -- эквивалентны; возврат константы.

Мейерс, кажется, писал про то, что возвращать по значению надо константы (чтобы нечаянно не вызвать что-то неконстантное, типа f().do_non_const(), что обычно абсурдно).

Но кто-то еще из гуру писал, что возвращать по значению надо non-const всегда, объясняя это тем, что тогда компилятор может лучше оптимизировать (то ли NRVO, то ли не создавать в каких-то случаях временную переменную).

5qmwpzuarnkntow2l.akvn.szoa2 and co: у const-функции const пишется в конце строки (int a::f() const)

0ptr комментирует...

1) Видел только использование, как описал ratson: защита от случайного = вместо == (что лично я считаю полным бредом).

2) Насчёт отличия, согласен с Андреем Валяевым:
int &var = foo(); // error!
Вот только смысла в этом не вижу. Переменная всё равно вернётся по значению, и почему запрещать её изменять - непонятно.

3) Опять же, как уже сказали, int const и const int вроде идентичны. Const-модификатор функции, конечно же, в конце пишется.
Единственная мысль, если играться с указателями, то
int const *
int * const
смотрятся более единообразно,чем
const int *
int * const
ну а если потом убрать *, то останется собственно int const :)

В общем, Алёна, рассказывайте уже своё мнение, уже достаточно бреда в комментах :)

Алексей комментирует...

1) Согласен что для защиты аргумента функции от случайного изменения. Правда для int x смысла не имеет, в отличие от int &x например. Наверное просто кодинг-стандарт, чтобы не задумываясь везде писать.

2,3) Имхо тоже эквивалентны. Думаю как const имеет смысл описывать функции-члены классов, которые не изменяют состояния объекта. Ну или вообще состояния чего-либо:)

malistov комментирует...

1) В том виде, что написано в вопросе, а именно

void MyFunction ( const int var);

это объявление функции, а, следовательно, ни на что const не влияет! Только если Вам лично что-то подсказывает.

В теле функции, если будет написана имплементация (а не декларация), будет так, как описал ratson, это убережет от изменения аргумента

void MyFunction ( const int var)
{
var = 5; // Error!
}


2, 3) Очевидно, эквиваленты. Кроме того, что это может уберечь от нежеланного присваивания, как об этом многие здесь, ссылаясь на Майерса, указали, есть случай, когда без const не будет работать. Если Вы используете шаблонные функции, то можете однажды натолкнуться на следующее:

void SomeFunc(int a);

template <class T>
void f1(T & a) {
....
SomeFunc(a); // для некоторых типов SomeFunc меняет a, для некоторых - нет.
}

const int ret_const_int() { return 0; }
int ret_int() { return 0; }

void f()
{
f1(ret_const_int()); // Нет ошибки, если SomeFunc не менят аргумент для int
f1(ret_int()); // ошибка
}

warenik комментирует...

1)Аргумент передаётся по значению - поэтому не меняется в любом случае. Я бы сказал, что это используется для того, чтобы показать, возможно другому программисту, что функция на аргумент не должна влиять даже при последующей модернизации.
2)тяжело объяснить словами - криво выходит:что-то вроде "чтобы нельзя было от возвращаемого значения что-то вызвать". мне запомнилась аргументация Майерса: представим себе, что у постфиксного оператора ++ не было бы const в начале: тогда возможно следующее
i++++;
i - объект класса, для которого определяется оператор. при наличии const - это невозможно.
короче я это использую, когда надо запретить:
obj.method1().method2();
а можно только:
obj.method1();
obj.method2();
где - method1() - возвращает объект такого же типа как и obj.
3)не встречал

Анонимный комментирует...

1) То же самое что писать const в объявлении любой локальной переменной.
2) и 3) с точки зрения языка эквивалентны. применительно к простому типу смысла не имеют. Смысл был бы, будь тут ссылки/указатели/объекты. А так - ни MyFunction().doSome(), ни MyFunction()=123, ни MyFunction=someOtherFunc не выполнишь. Можно еще подумать, что const int MyFunction() возвращает значение, не меняющееся от вызова к вызову (тогда то бы имело смысл для оптимизации компилятором), но вроде это в стандарте не определено.

Андрей Булавинов комментирует...

Ничего от этого не меняется. Так же как и в людях.

LG.BALUKATION комментирует...

Не вижу ничего странного в пером примере - ИМХО нормальная штука... Польза таких констант уже описана выше.

Анонимный комментирует...

1 - Очень удобная штука, тем более когда функция не должна менять переменную. Например
int hash(const string s) явно всем будет видно, что s не меняется. И не нужно передавать &s для скорости - при наличии const переменная сама передается по адресу.

gp комментирует...

«int hash(const string s) явно всем будет видно, что s не меняется. И не нужно передавать &s для скорости - при наличии const переменная сама передается по адресу.»

Ой ли?

White Knight комментирует...

По поводу п.1. Такой способ помогает, и довольно часто. Особенно при командной разработке :) За всеми ведь не уследишь.

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

Анонимный комментирует...

Т.е. нельзя будет написать
void f(cont int x){
x=1;
}
Хотя большой пользы от этого нет, разве что защита от случайного = вместо ==


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

Aleks_Z комментирует...

Начал вот читать книженцию "Шаблоны С++", справочник разработчика. Там авторы (Д.Вандервуд и Н.Джосаттис) пишут в самом начале: "
Для обозначения целочисленной константы мы решили применять несколько непривычный порядок записи – int const вместо const int. Сделано это было по двум причинам. Во-первых, такой порядок обеспечивает более очевидный ответ на вопрос: «Что именно является константой?». «Что» - это всегда то, что находиться перед модификатором const. однако для выражения

int *const bookmark; /* Указательн не может изменяться, однако может изменяться значение, на которое он указывает*/

не существует эквивалентной формы, в которой модификатор const стоял бы перед оператором указателя *, хотя
const int N = 100:
эквивалентно
int const N = 100;

В этом примере константой является сам указатель, а не целочисленное значение, на которое он указывает."

pm комментирует...

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

В прочем на плюсах давно не писал, чему очень рад :-).

Анонимный комментирует...

White Knight пишет...
По поводу п.1. Такой способ помогает, и довольно часто. Особенно при командной разработке :) За всеми ведь не уследишь.

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

Да ладно, всегда ли это полезно )?

... read_buf (const _TCHAR *s, ssize_t n)
{
if (n < 0)
n = _tcslen (s);
...
}

coff

Programmist комментирует...

1 - в целях оптимизации, разрешает компилятору передавать большие параметры по ссылке.
2 и 3 - явный запрет на использование результата функции в качестве l-value, т.е. результат функции не может быть слева от оператора присваивания. Что-то вроде vector.lenght() = 5;

Анонимный комментирует...

Для ператорных функций ничего странного а даже наоборот

template
T* operator-> () {}
const T* operator-> () {}

Анонимный комментирует...

pm пишет...

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

ты неправ

Alena комментирует...

2Анонимный

Для ператорных функций ничего странного а даже наоборот

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

Александр комментирует...

Например, компилятор на HP-UXах ругнется предупреждениями о "незначащем const" и в целом будет прав.