Info
-
Did you know that C++20 concepts can be used to avoid implicit conversions?
Example
#include <concepts>
void foo(int i);
void bar(std::same_as<int> auto i);
int main() {
foo(42.0); // implicit conversion
bar(42.0); // error: contraint not satisifed
}
Puzzle
-
Can you implement conversion safe
invoke[args...]
?- Note: Doesn't allow implicit conversions neither for parameters nor for the return type
struct {
template<class R, class... Ts>
[[nodiscard]] constexpr auto operator[](...); // TODO
} invoke;
auto foo(int, double, float) -> void;
constexpr auto can_invoke_with = []<class R, class... Ts>(R fn(Ts...), auto&&... args) {
return requires { { invoke.template operator[]<R, Ts...>(fn, std::forward<decltype(args)>(args)...) } -> std::same_as<R>; };
};
static_assert( can_invoke_with(foo, int{}, double{}, float{}));
static_assert(not can_invoke_with(foo, int{}, float{}, float{}));
static_assert(not can_invoke_with(foo, float{}, float{}, float{}));
static_assert(not can_invoke_with(foo, short{}, int{}, double{}, float{}));
static_assert(not can_invoke_with(foo, int{}, double{}, float{}, float{}));
Solutions
template<class R, class... Ts>
[[nodiscard]] constexpr auto operator[](R fn(Ts...), std::same_as<Ts> auto... ts ) -> R;
template<class R, class... Ts, class ...Ts2, ::std::enable_if_t<
sizeof...(Ts) == sizeof...(Ts2) &&
(::std::is_same_v<Ts, Ts2> && ...), int> = 0>
[[nodiscard]] constexpr auto operator[](R fn(Ts...), Ts2... ts ) -> R;
struct {
template <class R, class... Ts>
[[nodiscard]] constexpr auto operator[](R fn(Ts...),
std::same_as<Ts> auto&&... args)
-> std::same_as<R> decltype(auto) {
return fn(std::forward<Ts>(args)...);
}
} invoke;
struct {
template<class R, class... Ts>
[[nodiscard]] constexpr auto operator[](R fn(Ts...), std::same_as<Ts> auto&&... args){
return fn(std::forward<decltype(args)>(args)...);
}
} invoke;
struct {
template<class R, class... Ts>
[[nodiscard]] constexpr auto operator[](auto&& fn, std::same_as<Ts> auto&&... args) -> std::same_as<R> auto {
return fn(std::forward<Ts>(args)...);
}
} invoke;
struct {
template<class R, class... Ts>
[[nodiscard]] constexpr auto operator[](auto fn, std::same_as<Ts>auto ...params) -> std::same_as<R> auto {
return fn(params...);
};
} invoke;
struct {
template<class R, class... Ts>
[[nodiscard]] constexpr auto operator[](auto F, auto... args) -> std::same_as<R> auto requires (std::same_as<Ts,decltype(args)> && ...) {
return F(args...);
};
} invoke;
struct {
template<class R, class... Ts>
[[nodiscard]] constexpr auto operator[](R fn(Ts...), std::same_as<Ts> auto&& ... args)->R;
} invoke;
struct {
template<class R, class... Ts>
[[nodiscard]] constexpr auto operator[](auto f, std::same_as<Ts> auto &&...args){
f(args...);
}
} invoke;