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));
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());
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_;
};
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>;
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())>>;