Есть такой паттерн или стиль написания кода, как входные+выходные параметры у функции. Вместо того, чтобы возвращать результат работы функции в возвращаемом значении, его возвращают через ссылочные параметры.
И вот допустим, у нас программист Иннокентий. У него в проекте есть функция, которая хорошо и без нареканий работает и отправляет запрос базе
bool DBConnection::SendQuery(const char* query,
const DbQueryParams& params = DbQueryParams{});
Если запрос отослан удачно, возвращаем тру, если нет, то фолс.
Строка запроса к базе может содержать плейсхолдеры(это метки в подготовленном запросе, куда будут подставляться актуальные значения параметров), а может и не содержать. Если они есть, то вместе с query передаются и параметры для подстановки в запрос. Если нет, то у нас есть пустое значение по-умолчанию.
Но жизнь течет, все изменяется. Кенни поступила задачка проверять состояние конекшена перед отправкой запроса, возвращать результат проверки наружу и ...(построить какую-то логику на результате).
А в проекте у него используется паттерн с выходными параметрами. Обычно это реализовывается так: в начале в аргументах функции идут все входные параметры и затем все выходные. Вроде как логично.
Он, согласно своему код стайлу, пишет:
bool DBConnection::SendQuery(const char* query,
const DbQueryParams& params = DbQueryParams{},
DbConnectionState& state);
Реализовал функцию и запустил билд. А он, хромоногий, упал.
Нельзя указывать параметры функции без значения по умолчанию после параметров, имеющих это значение.
Видимо это сделано так, потому что иначе появляется пространство для неопределенности.
Ладно еще в этом случае компилятор видит разные типы и может как-то соотнести 2 и 2. Но например в случае тривиальных типов все не так однозначно. Они могут неявно конвертироваться друг в друга и тут уже распарсить нельзя.
void foo(int i, float j = 5.0, unsigned k);
foo(1, 2); // Невозможно понять, вызвали ли вы foo(1, 5.0, 2) или
например по ошибке передали слишком мало аргументов
Закатив глаза и особо не думая, он исправляет:
bool DBConnection::SendQuery(const char* query,
const DbQueryParams& params = DbQueryParams{},
DbConnectionState& state = DbConnectionState::OK);
И билд тоже падает! Но теперь уже по другому поводу.
Неконстантные левоссылочные параметры нельзя определять со значениями по-умолчанию.
Причина тут очень простая. Вы не можете создать левую ссылку из значения типа rvalue reference. У объекта должно быть имя, чтобы его можно было присвоить неконстантной левой ссылке. У DbConnectionState::OK имени нет, поэтому и упали.
Выход только один. Нарушать свой код-стайл. Придется пихать параметр DbConnectionState& state либо первым параметром, либо между query и params.
Первый способ вообще в принципе нарушает все негласные соглашения в объявлениях функции среди всех языков.
Второй вроде подходит, когда мы не передаем params. Но в случае, если в запросе есть плейсхолдеры и нам приходится передавать params, то выглядит все не менее ужасно.
Кенни не придумал ничего лучше и сильно расстроился. Пошел в лес, увидел там машину, сел в нее и сгорел.
А что Иннокентий в принципе мог сделать в этой ситуации? Жду ваших вариантов в комментариях)
Don't worry. All difficulties will pass.
#cppcore