Использование как const, так и не const для передачи параметров - PullRequest
2 голосов
/ 13 мая 2019

Если у меня есть объект

class Person {
  std::string name;
  // other important fields
  ... 
}

Теперь я хочу передать его в функцию с именем updateName

void updateName(std::string& mutableName, const Person& person)

Это допустимая функция? Я буду проходить так, как человек, мгновенный р:

updateName(p.name, p)

Это выглядит немного странно для меня, так как я говорю, что я изменю имя, но второй параметр указывает на то, что это const. Очевидно, я могу передать только изменяемый экземпляр Person. Проблема в том, что это большая система, и я не могу изменить существующий интерфейс, но добавить в него параметры.

Ответы [ 2 ]

4 голосов
/ 13 мая 2019

Это может показаться странным, но это нормально. const Person& person означает, что person равно const в updateName, это не означает, что объект person, который вы передали функции, теперь является постоянным.

Если бы p было const в вызываемом вами коде, то p.name не сработало бы, поскольку оно имело бы тип const std::string, и вы не можете привязать ссылку на non const к объекту const.


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

0 голосов
/ 13 мая 2019

Действительна функция, подпись которой:

void updateName(std::string & mutableName, const Person & person);

.Однако вы рискуете столкнуться с неопределенным поведением, если mutableName является частью person.Рассмотрим эти примеры из Википедии (https://en.wikipedia.org/wiki/Undefined_behavior):

i = i++ + 1;  // undefined behavior
a[i] = i++;  // undefined behavior

. Объяснение с учетом того, что они являются неопределенным поведением:

Modifying an object between two sequence points more than once produces undefined behavior.

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

Несколько лет назад мне сказали, что ключевое слово const было контрактом между программистом и человеком.компилятор, что переменная не изменится. И здесь, в updateName(), мы видим, что это const соглашение используется со вторым аргументом, поэтому мы (программисты) обязаны поддерживать это соглашение.

Имейте в виду, что есть хитрый способ обойти это const соглашение, которое вы можете найти, если приложите достаточно усилий, но они почти всегда рискуют неопределенным поведением. Один из таких способов, как вы показали, состоит в том, чтобы пройтиp.name в качестве первого аргумента при передаче p в качестве второго.

Если вы действительно хотите использовать вызов, например:

updateName(p.name, p);

, тогда было бы хорошо, если бы выИзвлеките токен & из подписи, чтобы он выглядел так:

void updateName(std::string & mutableName, const Person person);

Таким образом, передается копия Person, и изменение mutableName не будет мешатьperson.

Или вы можете сделать оба аргумента const & и просто вернуть новое имя, например:

// Will return a newer, better name to use:
std::string updateName(const std::string & name, const Person & person);

Теперь вы можете использовать вызов, подобный следующему:

p.name = updateName(p.name, p);

и не нужно беспокоиться об изменениях p.name, мешающих поведению updateName().


EDIT:

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

Я хотел бы отметить, что, в то время как person может технически изменить, это не будет очевидно для сопровождающего (-ых), поддерживающего этот код.В конце концов, он объявлен объявленным const, и поэтому некоторые (или большинство?) Знающих сопровождающих (ошибочно) предположат, что его значение не изменится внутри функции.Мало того, но когда они находятся за пределами вызова функции и смотрят на сам вызов функции, это тоже не слишком очевидно.

Я сталкивался с такими ошибками, когда поддерживал код: переданный объект(не обязательно const) загадочным образом изменен атрибут.«Нет проблем, - думаю я, - я просто найду строку кода, которая модифицирует этот атрибут, и исправлю его».Как же я ошибся ... Большую часть дня, проведенного с досадной отладкой каждой строки, я обнаружил, что атрибут меняется не тогда, когда атрибут назначен или используется , а , когдаВызывается совершенно другая строка кода.

Это приводит к странным и трудным для поиска ошибкам.

Я замечаю, что из вашей функции updateName() ничего не возвращается.Это идеальное место для возврата обновленного name.

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

Если вы действительно хотите передать person, не рискуя изменить его, вы можете передать копию person с:

updateName(p.name, Person(p));

Person(p) вызовет конструктор копирования Person и оставит оригинал p в покое.

Это похоже на вызов функции, котораяпринимает константную ссылку, например:

// Returns double of the input:
int doubler(const int & n)
{
    return n * 2;
}

Некоторые люди думают, что вы можете передавать только целочисленные переменные, тогда как целочисленные литералы (например, 7 и -12) запрещены.

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

Итак, когда вы сказали в своем посте:

Obviously I can only pass in a mutable Person instance.

Я не уверен, что согласен здесь.Попробуйте передать Person(p) (вместо p) и посмотреть, скомпилируется ли это.Тогда вы можете вызывать вашу updateName() функцию, не опасаясь изменения вашего p объекта.

(То есть, если не модификация p - это действительно то, что вы хотите.)

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

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