Действительна функция, подпись которой:
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
- это действительно то, что вы хотите.)
Я надеюсьэто помогает, и извините за любую путаницу, вызванную первой частью моего ответа.