Обсуждение ссылок в параметрах


#1

Ссылки вообще в параметрах передавать не стоит в первом юзкейсе, т.к. выходные параметры стоит таки видеть при чтении кода. Действительно,

//y - out
void some_function_1(int x, int&y) { ... }

void some_function_2(int x, int*y) { ... }

some_function_1(x, y); //1
some_function_2(x, &y); //2

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

P.S. IMHO.


Стандартная библиотека C++ (2015)
#2

@JediKnight с Вами мало кто согласится. Ссылки очень удобный механизм и точечная нотация читается приятнее чем стрелочная.

Кроме того, я рассказывал на лекции зачем их ввел Комитет в C++, а Вы их уже убрать хотите на 50% :slight_smile:


#3

Хочу.

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

mov ax, bx

Смогли бы Вы, впервые взглянув на этот код, сказать, какой из параметров этой мнемоники входной, а какой выходной(предположим, что Вы ассемблера совершенно не знаете)? Скажу честно - 2 курса было с его использованием, а все равно толком не помню, какой из них кто :smile:

Переменные, переданные в С++ подобным образом, создают такой же точно эффект. К примеру, посмотрим на copy_value(a, b). Сказать, что и куда копируется без чтения функции попросту невозможно. Изменив сигнатуру на void copy_value(const val& x, val* y) , мы это обозначим явным образом: кроме как copy(a, &b), написать никак нельзя.

Остается добавить только цитату из общепринятого во многих сообществах программистов [Google C++ Style Guide][1]:

Reference Arguments

All parameters passed by reference must be labeled const.

Definition

In C, if a function needs to modify a variable, the parameter must use a pointer, eg *int foo(int pval). In C++, the function can alternatively declare a reference parameter: int foo(int &val).

Pros

Defining a parameter as reference avoids ugly code like (*pval)++. Necessary for some applications like copy constructors. Makes it clear, unlike with pointers, that a null pointer is not a possible value.

Cons

References can be confusing, as they have value syntax but pointer semantics.

Desicion

Within function parameter lists all references must be const:

void Foo(const string &in, string *out);


>In fact it is a very strong
> convention in Google code that input arguments are values or const
> references while output arguments are pointers. Input parameters may
> be const pointers, but we never allow non-const reference parameters
> except when required by convention, e.g., swap().
> 
> **However, there are some instances where using const T* is preferable to const T& for input parameters. For example:**
> 
> You want to pass in a null pointer. The function saves a pointer or
> reference to the input. Remember that most of the time input
> parameters are going to be specified as const T&. Using const T*
> instead communicates to the reader that the input is somehow treated
> differently. So if you choose const T* rather than const T&, do so for
> a concrete reason; otherwise it will likely confuse readers by making
> them look for an explanation that doesn't exist.


  [1]: https://google-styleguide.googlecode.com/svn/trunk/cppguide.html#Reference_Arguments

#4

Это плохой аргумент. Помнится гугл где-то в своих проектах ООП на плюсах запрещал, так что всем надо на это ориентироваться? А Линус Торвальдс вообще терпеть плюсы не может.

Кроме того, вспомним .Net (кажется Вами любимый). Так там вообще все объекты по ссылке и никого не смущает.

И повторюсь про Комитет. Ссылки были разработаны для двух вещей: а) передача маленького объекта, вместо копирования большого б) возможность редактировать переданные объекты (а-ля var параметры). Вы вместе с гуглом не хотите использовать б - ваше право. Я уверен, что это не все разделяют. Например, Pratta! И Meyers приводит код со ссылками и не помню, чтобы он советовал их упразднить.


#5

Не знаю, где он общепринят, про него уже давно очень нелестно отзываются, например (либо на SO поищите в обсуждениях).

К тому же, там написано (то, что вы выделили), что если на это (* вместо &) нет ну очень явной причины, такой подход скорее собьет с толку человека, читающего код, нежели поможет. Как-то не сходится :smile:


#6

Там не это написано. Учим английский и вдумываемся в текст :smile:


#7

Using const T* instead communicates to the reader that the input is somehow treated differently. So if you choose const T* rather than const T&, do so for a concrete reason; otherwise it will likely confuse readers by making them look for an explanation that doesn’t exist.

По–моему тут явно написано то, что я сказал.


#8

По поводу входных параметров у меня к ссылкам претензий не было! Обижаете, сударь :smile:


#9

@sanya_rnd Мне тоже кажется, что Вы немного напутали. @JediKnight утверждает, что выходные параметры лучше передавать указателями, т.е. T* вместо T&. А в том куске текста речь идет о входных параметрах, т.е. const T&, и просят не писать const T*.


#10

Да, я уже увидел. Беглое чтение - враг мой.


#11

Согласен с @JediKnight в том, что использование указателя при передаче параметра даёт понять пользователю, что значение данного аргумента будет модифицировано.

@RS

просят не писать const T*

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


#12

Я сознательно “сказку сократил” для того чтобы пояснить. Остальное детали.


#13

А вообще по теме, я скажу вот что. В конце 90-х когда я слушал лекции по С++ Станислава Станиславовича, я бы согласился с этой концепцией сразу. Сейчас совсем другое время и я другой. Вы хотите видеть изменяемые параметры при передаче. А я бы хотел вообще, чтобы при программировании на С++ меня за каждым углом не поджидал злобный зубастый монстр, норовящий оттяпать мне то ногу, то руку. “Хочется видеть output-ы” - не аргумент. Зачем Вам это? Т.е. теоретически это полезно, приведите практический пример. Кстати, я всегда смотрю на прототип функции, а не на ее вызов.

Мои аргументы против вашего подхода:

  1. Это усложняет текст лишними символами

  2. Сегодня умные указатели и легковесные объекты (см SDValue в llvm) вытесняют обычные указатели. Хотел бы я посмотреть, как Вы для них сообщите, что они выходные параметры

  3. Посмотрите.на STL. Вы там видите указатель на vector? А ее я буду использовать! И что ж мне смешивать стили google и stl? Нет уж спасибо!


#14

Примеры кода я выше приводил, по идее, они каноничны в данном вопросе. Хороший аргумент - определенная с указателем функция попросту не даст передать ничего кроме указателя - соответственно, придется лишний раз вспомнить о том, что она этот параметр меняет. А с ссылкой можно передать, например другой объект(или попросту вход и выход поменять местами), и компилятор это съест, не задумываясь, что приведет к отсутствию ошибки компиляции и долгому поиску ошибки(если проект большой, и рассчитываются, например, какие-нибудь математические выражения с малыми значениями - дисперсии, как вариант), т.к. явно практически ничего не изменится, только конечный результат будет неверен. Т.е. даже в процессе написания собственного кода, а не чтения чужого, это может быть опасно. В C#, например, есть ключевое слово out - думаю, оно бы решало все подобные проблемы, и очень странно, что его в С++ нет.

В STL в том или ином варианте и так указатели используются - .begin(), .end(), … - пусть даже это и итераторы, смысл у них тот же. Но проблем описанных они не решают, что заставляет устраивать пляски с const_iterator, которые лишь смягчают удар и заставляют писать кучу совершенно не имеющего функционального смысла кода. Вообще, считаю, что в STL есть сотни способов стрелять себе в ногу, и не уверен, что это хорошо. Писать на самом деле безопасный код, и быть на 100% уверенным в отсутствии ошибок, используя исключительно STL, по моему мнению, очень сложно. Собственно, ради этого различные стайлгайды и придуманы - отказаться намеренно от какой-либо функциональности ради упрощения итераций тестирования вполне оправданно.

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

Кстати, а Вы, случаем, не собираетесь читать про умные указатели на лекциях? Я б забежал, т.к. общие знания есть, но они какие-то… поверхностные.


#15

Я полностью согласен, что С++ сложный и неудобный язык. На нем трудно писать с уверенностью 100% (лично я вообще всегда в себе сомневаюсь, хотя бы на маленькую толику). И безусловно то, что мы сейчас обсуждаем - это холивар чистой воды. Я к сожалению, не очень понял аргументы в Вашем последнем посте (дисперсии, итераторы …), но я не возражаю против того, что Ваша (и гугла) точка зрения имеет право на жизнь. Но я понимаю, желание уменьшить степень свободы в использовании языка. Просто Вы должны понимать, что Ваша точка зрения не единственна и у оппонентов тоже есть аргументы. Это будет более объективно.

Коротко о том, что понял. Про out в C# все просто. У плюсов есть T, T*, T&, шарп обойтись только первым вариантом объективно не мог (даже с учетом ссылочной модели). Вот они его и добавили. Если его добавить в плюсы, то получится аж 4 варианта (или не дай бог 3*2) передачи параметров. Это перебор.

Я не говорил “исключительно STL”, я говорил, что его использовать однозначно буду. Почитайте Мейерса и поймете что STL очень хорош. В C# эта вся кухня гораздо беднее.

Про умные указатели буду рассказывать, но в ключе использования, а не реализации. Если Вас интересует реализация, то или почитайте кого-нибудь или приходите могу показать как это устроено в ОРС. Ну, а если есть желание прийти на лекцию, то “добро пожаловать”.


#16

Просто кое-кто слишком много программирует на C.


#17

Каюсь. :smile: