Здравствуйте, LaptevVV, Вы писали:
LVV>Например: https://cpppatterns.com/patterns/optional-arguments.html
Интересно, у вас специально получилось дать ссылку на крайне спорный паттерн?
This approach is more expressive than using pointers and nullptr.
Если брать пример использования:
foo(5, std::nullopt, std::nullopt);
то это ничем не лучше:
foo(5, nullptr, nullptr);
Выразительнее было бы что-то вроде:
foo(5, no_f_specified, no_b_specified);
Где вместо `no_f_specified` и `no_b_specified` будут вменяемые, привязанные к прикладной области, именованные константы.
Вроде `default_scale` и `no_pretty_printing`:
foo(5, default_scale, no_pretty_printing);
По поводу выразительности самой декларации:
void foo(int i,
std::optional<double> f,
std::optional<bool> b);
Тоже не однозначно, т.к. если придерживаться правила современного С++ о том, что голые указатели только невладеющие, то декларация:
void foo(int i,
const double * f,
const bool * b);
такая же понятная: качестве f и b допускается передача nullptr, если бы не допускалась, то вместо указателя была бы ссылка.
И еще вопрос о том, а что эффективнее -- передавать указатель на опциональный double или же std::optional для double. Ведь в C++ std::optional налагает плату в виде дополнительного bool-а, тем самым увеличивая размер параметра.
Однозначный выигрыш от std::optional есть когда мы передаем опциональный аргумент по значению, а не по ссылке/указателю. Что-то вроде:
struct MyData { ... };
struct ProcessingParams { ... };
void process(MyData what,
// Принимаем по значению.
std::optional<ProcessingParams> how) {...}
std::optional<ProcessingParams> // Возвращаем по значению.
try_find_custom_params() {...}
process(my_data, try_find_custom_params());
Вот в этом случае да, аргумент в виде optional удобнее, нежели указатель.
Но про такой сценарий по ссылке и не сказано
Здравствуйте, so5team, Вы писали:
S>This approach is more expressive than using pointers and nullptr.
S>Если брать пример использования:
S>S> foo(5, std::nullopt, std::nullopt);
S>
S>то это ничем не лучше:
S>S> foo(5, nullptr, nullptr);
S>
Можно писать
foo(5, {}, {});
но эт вкусовщина.
А "паттерн", да, кривой и вообще не паттерн.
optional подходит если нет особенного значения типа nullptr, 0, -1, пустой строки и т.д. То есть, эти значения тоже являются значениями, и
foo(5, nullptr, nullptr) не эквивалентно
foo(5, {}, {})
Иначе зачем он нужен?... Шляпа.
Здравствуйте, LaptevVV, Вы писали:
LVV>Например: https://cpppatterns.com/patterns/optional-arguments.html
foo(5, 1.0, true);
foo(5, std::nullopt, true);
foo(5, 1.0, std::nullopt);
foo(5, std::nullopt, std::nullopt);
Не читабельно совсем.
Как вариант:
#include <optional>
struct foo_options
{
std::optional<double> f;
std::optional<bool> b;
};
void foo(int, foo_options = {});
int main()
{
foo(5, {.f = 1.0, .b = true});
foo(5, {.f = 1.0});
foo(5, {.b = true});
foo(5);
}
LVV>>Например: https://cpppatterns.com/patterns/optional-arguments.html
S>Интересно, у вас специально получилось дать ссылку на крайне спорный паттерн?
Вот и обсуждение началось!
Прекрасно!
Здравствуйте, LaptevVV, Вы писали:
LVV>https://cpppatterns.com/
LVV>Не только классические.
По большей части это не паттерны, а приёмы/идиомы на С++, причём для начинающих.
Очень много чего нет:
например: signal-slot паттерн (который не Qt), потокобезопасная очередь, да даже factory отсутствует...
Здравствуйте, so5team, Вы писали:
можно накрутить с variant и ignore, получается вроде как более читабельно
void foo(std::variant<decltype(std::ignore), int> fst, decltype(std::ignore), float> snd);
...
foo(std::ignore, 1.0);
foo(1, std::ignore);
хотя я предпочитаю заводить новые типы и у них уже добавлять значение по умолчанию
enum class NewType1 {Default = -1, Empty = -1};
enum class NewType2 {Default = -1, Empty = -1};
void foo(NewType1 fst, NewType2 snd);
foo(NewType1{1}, NewType2::Default);