Аргумент по умолчанию против std::Optional в качестве аргумента против перегрузки функции

Допустим, я реализую функцию func.

  • У меня есть вариант использования, когда func будет вызываться с двумя параметрами.
  • У меня есть еще один вариант использования, когда func будет вызываться только с одним параметром, но внутри функции будет использоваться значение по умолчанию для отсутствующего второго параметра.

Я могу придумать 3 возможных способа реализации этой функциональности:

  1. Аргумент по умолчанию
void func(int a, long b = c);

func будет вызываться следующим образом:

func(a);
func(a, b);
  1. Перегрузка функций
void func(int a);
void func(int a, long b);

func будет вызываться следующим образом:

func(a);
func(a, b);
  1. Использование необязательного в качестве аргумента функции
void func(int a, optional<long> b);

func будет вызываться следующим образом:

func(a, optional<long> ());
func(a, b);

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

Вариант 3 лучше, чем кажется на вопросе. И {}, и nullopt работают.

chris 31.05.2019 04:53

Вы спрашиваете о читабельности? Если это так, я думаю, что аргумент по умолчанию великолепен, потому что он показывает выбор используемого значения в определении.

Jeremy Owens 31.05.2019 04:59
Стоит ли изучать PHP в 2023-2024 годах?
Стоит ли изучать PHP в 2023-2024 годах?
Привет всем, сегодня я хочу высказать свои соображения по поводу вопроса, который я уже много раз получал в своем сообществе: "Стоит ли изучать PHP в...
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
В JavaScript одним из самых запутанных понятий является поведение ключевого слова "this" в стрелочной и обычной функциях.
Приемы CSS-макетирования - floats и Flexbox
Приемы CSS-макетирования - floats и Flexbox
Здравствуйте, друзья-студенты! Готовы совершенствовать свои навыки веб-дизайна? Сегодня в нашем путешествии мы рассмотрим приемы CSS-верстки - в...
Тестирование функциональных ngrx-эффектов в Angular 16 с помощью Jest
В системе управления состояниями ngrx, совместимой с Angular 16, появились функциональные эффекты. Это здорово и делает код определенно легче для...
Концепция локализации и ее применение в приложениях React ⚡️
Концепция локализации и ее применение в приложениях React ⚡️
Локализация - это процесс адаптации приложения к различным языкам и культурным требованиям. Это позволяет пользователям получить опыт, соответствующий...
Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
0
2
425
1
Перейти к ответу Данный вопрос помечен как решенный

Ответы 1

Ответ принят как подходящий

Есть два вопроса, которые нужно задать себе при рассмотрении этого вопроса:

  • Имеет ли необязательный аргумент логическое значение по умолчанию?
  • Реализована ли функция одинаково, когда указан необязательный аргумент, а когда нет?

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

std::vector<std::string> split_string(const std::string& str, char sep = ' ');

По умолчанию это разбивает строку на пробелы, но для изменения этого можно указать разделитель. Нет особого смысла разбивать это на две перегрузки или использовать здесь std::optional.


Если второй аргумент не имеет логического значения по умолчанию, но функция остается в основном такой же, если он не задан, то std::optional имеет больше смысла. Например:

void extract(const std::filesystem::path& archive_file,
             const std::filesystem::path& output_dir,
             std::optional<std::regex> exclude_filter = {});

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

if (exclude_filter && std::regex_match(file, *exclude_filter) continue;

Дублировать определение не имеет особого смысла, поэтому перегрузка не имеет смысла. В то же время не существует регулярного выражения «ничего не соответствует», поэтому нет логического фильтра по умолчанию, который можно было бы применить. std::optional идеально подходит здесь.

Обратите внимание, что я по-прежнему использовал аргумент по умолчанию. Если по умолчанию ваш std::optional пуст, это может сделать ваши звонки намного приятнее.


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

void hide(window& win) {
    win.set_visible(false);
}

template <typename rep, typename period>
void hide(window& win, const std::chrono::duration<rep, period>& fade_time) {
    auto wait_time = equal_intervals(fade_time, win.opacity());
    while (win.oapcity() > 0) {
        win.set_opacity(win.opacity() - 1);
        std::this_thread::sleep_for(wait_time);
    }
}

Здесь мы скрываем окно с необязательным временем затухания. Хотя логически функции делают одно и то же, они реализованы совершенно по-разному. Быть одной и той же функцией не имеет большого смысла. Несмотря на то, что 0 было бы логическим временем затухания по умолчанию, по-прежнему не имеет смысла использовать здесь аргумент по умолчанию, поскольку вы просто получите большой блок if:

if (fade_time == 0) {
    // body of the first overload
} else {
    // body of the second overload
}

Спасибо за подробное объяснение, Майлз :)

Will_of_fire 31.05.2019 20:42

Другие вопросы по теме