Skip to content

Latest commit

 

History

History
179 lines (134 loc) · 4.08 KB

318.md

File metadata and controls

179 lines (134 loc) · 4.08 KB
Info

Example

struct interface {
    constexpr virtual ~interface() = default;
    constexpr virtual auto get() const -> int = 0;
};

struct implementation final : interface {
    constexpr explicit(true) implementation(int value) : value{value} {}
    constexpr auto get() const -> int { return value; }

private:
    int value{};
};

constexpr auto foo(auto value) {
    std::unique_ptr<interface> i = std::make_unique<implementation>(value);
    return i->get();
}

static_assert(42 == foo(42));

https://godbolt.org/z/vW9n4EzM8

Puzzle

Can you implement constexpr std::function?

// TODO: function

consteval auto test_empty() {
  function f = [] { return 42; };
  return f();
}

consteval auto test_arg() {
  function f = [](int i) { return i; };
  return f(42);
}

consteval auto test_capture() {
  int i = 42;
  function f = [&] { return i; };
  return f();
}

static_assert(42 == test_empty());
static_assert(42 == test_arg());
static_assert(42 == test_capture());

https://godbolt.org/z/e8h1rv3Ee

Solutions

template<typename F>
struct function {
   consteval function(F&& f) : f_{std::forward<F>(f)} {}
   template<typename ...Args>
   consteval auto operator()(Args&&... args) requires std::invocable<F, Args...>
   {
       return f_(std::forward<Args>(args)...);
   }

   F f_;
};

https://godbolt.org/z/4hjx5aYfP

template <class>
class function;

template <class R, class... TArgs>
class function<R(TArgs...)> {
  struct interface {
    constexpr virtual auto operator()(TArgs...) -> R = 0;
    constexpr virtual ~interface() = default;
  };

  template <class Fn>
  struct implementation final : interface {
    constexpr explicit(true) implementation(Fn fn) : fn{fn} {}
    constexpr auto operator()(TArgs... args) -> R { return fn(args...); }

   private:
    Fn fn{};
  };

 public:
  template <class Fn>
  constexpr function(Fn fn) : fn{std::make_unique<implementation<Fn>>(fn)} {}

  constexpr auto operator()(TArgs... args) const -> R {
    return (*fn)(args...);
  }

 private:
  std::unique_ptr<interface> fn{};
};

template <class> struct function_traits {};

template <class R, class B, class... TArgs>
struct function_traits<R (B::*)(TArgs...) const> {
  using type = R(TArgs...);
};

template <class F>
function(F) -> function<typename function_traits<decltype(&F::operator())>::type>;

https://godbolt.org/z/s3bPhha7h

template <class>
class function;

template <class R, class... Args>
struct function<R(Args...)> {
  template <class F>
  constexpr function(F f) : ptr{std::make_unique<implementation<F>>(f)} {}

  constexpr auto operator()(Args... args) const -> R {
    return ptr->get(args...);
  }

 private:
  struct interface {
    constexpr virtual auto get(Args...) -> R = 0;
    constexpr virtual ~interface() = default;
  };

  template <class F>
  struct implementation final : interface {
    constexpr explicit(true) implementation(F f) : f{f} {}
    constexpr auto get(Args... args) -> R { return f(args...); }

   private:
    F f;
  };

  std::unique_ptr<interface> ptr;
};

// https://en.cppreference.com/w/cpp/utility/functional/function/deduction_guides

template <class>
struct function_traits {};

template <class R, class G, class... A>
struct function_traits<R (G::*)(A...) const> {
  using function_type = R(A...);
};

template <class F>
using function_type_t = typename function_traits<F>::function_type;

// This overload participates in overload resolution only if &F::operator() is
// well-formed when treated as an unevaluated operand and
// decltype(&F::operator()) is of the form R(G::*)(A...) (optionally
// cv-qualified, optionally noexcept, optionally lvalue reference qualified).
// The deduced type is std::function<R(A...)>.
template <class F>
function(F) -> function<function_type_t<decltype(&F::operator())>>;

https://godbolt.org/z/4KhzajrKE