overloaded.h 1.7 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556
  1. #pragma once
  2. /**
  3. * Construct an ad-hoc object with an overloaded `operator()`.
  4. *
  5. * Typically used with lambdas to construct type-matching visitors for e.g. std::variant:
  6. * ```
  7. * std::variant<int, void*, TString> var;
  8. * Visit(TOverloaded{
  9. * [](int val) { Cerr << "int: " << val; },
  10. * [](void* val) { Cerr << "ptr: " << val; },
  11. * [](const TString& val) { Cerr << "str: " << val; },
  12. * }, var);
  13. * ```
  14. *
  15. * *** IMPORTANT NOTE (IMPLICIT ARGUMENT CONVERSIONS) ***
  16. *
  17. * Since the resulting objects use regular overloaded method resolution rules,
  18. * methods may be called by inexact matches (causing implicit casts), hence this
  19. * implementation does not guarantee exhaustiveness of cases.
  20. *
  21. * For example, the following code will compile and run by casting all values to
  22. * double:
  23. * ```
  24. * std::variant<int, double, char> var;
  25. * Visit(TOverloaded{
  26. * [](double val) { Cerr << "dbl: " << val; },
  27. * }, var);
  28. * ```
  29. *
  30. * If cases may be ambigous or specific type-matching logic is required,
  31. * a verbose `if constexpr`-based version would be preferred:
  32. * ```
  33. * std::variant<int, double, char> var;
  34. * Visit([](auto&& val) {
  35. * using T = std::decay_t<decltype(val)>;
  36. * if constexpr (std::is_same_v<T, int>) {
  37. * Cerr << "int: " << val;
  38. * } else if constexpr (std::is_same_v<T, double>) {
  39. * Cerr << "dbl: " << val;
  40. * } else if constexpr (std::is_same_v<T, char>) {
  41. * Cerr << "chr: " << val;
  42. * } else {
  43. * static_assert(TDependentFalse<T>, "unexpected type");
  44. * }
  45. * }, var);
  46. * ```
  47. */
  48. template <class... Fs>
  49. struct TOverloaded: Fs... {
  50. using Fs::operator()...;
  51. };
  52. template <class... Fs>
  53. TOverloaded(Fs...) -> TOverloaded<Fs...>;