英文:
is there a std::optional_function like the given
问题
I am searching for something like swifts ? operator in c++ for std::function. I have grown to like it over the last couple years.
我正在寻找类似于C++中的std::function的Swift的?运算符。在过去的几年中,我已经喜欢上了它。
I would like a std::optional_function, which only calls the function if the function exists.
我想要一个std::optional_function,只有在函数存在时才调用该函数。
Something like this (but written by the gods of c++):
类似于这样的东西(但由C++之神编写):
Another revision
另一个版本
Here is revision 2. In order not to polute the question, and since I don't know if this will be a final answer, I'm gonna place it here for now:
这是第二个版本。为了不污染问题,并且因为我不知道这是否会成为最终答案,我现在将其放在这里:
Ok one more version, which uses basically optional to get rid of some edge cases.
好,再来一个版本,基本上使用optional来消除一些边缘情况。
英文:
I am searching for something like swifts ? operator in c++ for std::function. I have grown to like it over the last couple years.
I would like a std::optional_function, which only calls the function if the function exists.
Something like this (but written by the gods of c++):
template<typename R>
struct option_function_result {
bool executed;
R result;
} ;
template<>
struct option_function_result<void>
{
bool executed;
} ;
template<typename F>
class optional_function
{
public:
typedef std::function<F> function_type;
typedef option_function_result<typename function_type::result_type> result_type;
protected:
function_type f;
public:
template<typename Fn>
optional_function operator=(const Fn &f_)
{
f = f_;
return *this;
}
template<typename Fn>
optional_function operator=(Fn &&f_)
{
f = std::forward<Fn>(f_);
return *this;
}
operator bool() const
{
return (bool)f;
}
template<typename ...Args, typename R>
result_type operator()(Args... args)
{
if (f)
return result_type { true, f(args...) };
return result_type { false };
}
template<typename ...Args>
result_type operator()(Args... args)
{
if (f)
{
f(args...);
return result_type { true };
}
return result_type { false };
}
} ;
Another revision
Here is revision 2. In order not to polute the question, and since I don't know if this will be a final answer, I'm gonna place it here for now:
I expect that the constructor for the struct is not necessary. However it forces the compiler to give me errors I need to debug the compilation.
template<typename R>
struct optional_function_result {
bool executed;
R result;
optional_function_result(bool &&executed_, R &&result_) :
executed (executed_),
result(result_) {}
} ;
template<>
struct optional_function_result<void>
{
bool executed;
optional_function_result(bool &&executed_) :
executed (executed_) {}
} ;
template<typename F>
class optional_function
{
public:
typedef std::function<F> function_type;
typedef typename std::function<F>::result_type function_result_type;
typedef optional_function_result<typename function_type::result_type> result_type;
protected:
function_type f;
public:
template<typename Fn>
optional_function operator=(const Fn &f_)
{
f = f_;
return *this;
}
template<typename Fn>
optional_function operator=(Fn &&f_)
{
f = std::forward<Fn>(f_);
return *this;
}
operator bool() const
{
return (bool)f;
}
template<
typename ... Args,
typename FR=function_result_type,
typename std::enable_if<!std::is_void<FR>::value, FR>::type* = nullptr
>
result_type operator()(Args... args) const
{
if (f)
return {
true,
std::forward<typename function_type::result_type>(f(args...))
};
return {
false,
function_result_type()
};
}
template<
typename ... Args,
typename FR=function_result_type,
typename std::enable_if<std::is_void<FR>::value, FR>::type* = nullptr
>
result_type operator()(Args... args) const
{
if (f)
{
f(args...);
return { true };
}
return { false };
}
} ;
Ok one more version, which uses basically optional to get rid of some edge cases.
template<typename T>
using optional_type = std::experimental::optional<T>;
template<typename R>
struct optional_function_result : optional_type<R> {
typedef optional_type<R> super_type;
optional_function_result() :
super_type() {}
optional_function_result(R &&result_) :
super_type(result_) {}
bool executed() const { return this->has_result(); }
} ;
template<>
struct optional_function_result<void>
{
bool executed_;
optional_function_result(bool &&executed__) :
executed_ (executed__) {}
bool executed() const { return executed_; }
} ;
template<typename F>
class optional_function
{
public:
typedef std::function<F> function_type;
typedef typename std::function<F>::result_type function_result_type;
typedef optional_function_result<typename function_type::result_type> result_type;
protected:
function_type f;
public:
template<typename Fn>
optional_function operator=(const Fn &f_)
{
f = f_;
return *this;
}
template<typename Fn>
optional_function operator=(Fn &&f_)
{
f = std::forward<Fn>(f_);
return *this;
}
operator bool() const
{
return (bool)f;
}
template<
typename ... Args,
typename FR=function_result_type,
typename std::enable_if<!std::is_void<FR>::value, FR>::type* = nullptr
>
result_type operator()(Args... args) const
{
if (f)
return {
std::forward<typename function_type::result_type>(f(args...))
};
return {};
}
template<
typename ... Args,
typename FR=function_result_type,
typename std::enable_if<std::is_void<FR>::value, FR>::type* = nullptr
>
result_type operator()(Args... args) const
{
if (f)
{
f(args...);
return { true };
}
return { false };
}
} ;
答案1
得分: 3
?
运算符在C++中也非常有效:
// 让函数的类型为std::function或函数指针
auto var = f ? f() : default_value;
如果你真的想要一个能够做到这一点的类型,在标准库中是没有这样的东西的,但一个简单的函数就足够满足你的需求(只适用于不返回引用或void的函数):
template<typename F, typename... Args, typename R = std::invoke_result_t<F, Args&&...>>
auto optionally_call(F&& f, Args&&... args) -> std::optional<R> {
return f ? R(std::forward<F>(f)(std::forward<Args>(args)...)) : std::nullopt;
}
通过一些元编程,可以支持此实现不支持的情况。
这是为了强调在创建一个通用类型时存在许多陷阱。存在许多错误和性能问题,甚至在你的示例代码中可能无法调用的代码。一个简单的实用函数比一个类型更容易。
英文:
The ?
operator works really well in C++ too:
// let function be of type std::function or a function pointer
auto var = f ? f() : default_value;
If you really want a type that does that, there is no such thing in the standard library, but a simple function is enough to do what you want (works only for function that don't return references or void):
template<typename F, typename... Args, typename R = std::invoke_result_t<F, Args&&...>>
auto optionally_call(F&& f, Args&&... args) -> std::optional<R> {
return f ? R(std::forward<F>(f)(std::forward<Args>(args)...)) : std::nullopt;
}
With some metaprogramming, it's possible to support cases not supported by this implementation.
This is to highlight that there's a lot of pitfalls when creating a whole type that is meant to be generic. There are many mistakes and performance issues and even code that will cannot be called in your sample code. A simple utility function would be easier than a type.
答案2
得分: 1
标准库中没有类似的功能,但您可以自己构建一个:
#include <functional>
#include <iostream>
#include <optional>
template <typename T>
class optional_function {
private:
std::optional<T> func;
public:
optional_function(T f) : func{std::move(f)} {}
optional_function() = default;
template <typename... Args>
auto operator()(Args&&... args) const {
using func_invoke_type = decltype((*func)(std::forward<Args>(args)...));
constexpr bool func_invoke_type_is_void = std::is_same_v<void, func_invoke_type>;
using optional_result_type = std::optional<
std::conditional_t<
func_invoke_type_is_void,
char,
std::conditional_t<
std::is_reference_v<func_invoke_type>,
std::reference_wrapper<std::remove_reference_t<func_invoke_type>>,
func_invoke_type
>
>
>;
if (func) {
if constexpr (!func_invoke_type_is_void) {
return optional_result_type{(*func)(std::forward<Args>(args)...)};
} else {
(*func)(std::forward<Args>(args)...);
return optional_result_type{ '\0' }; // 无法返回 void{}
}
}
return optional_result_type{};
}
};
// 测试
void foo() {}
int main() {
optional_function f1{[](int i) { return i * i; }};
optional_function f2{[] { std::cout << "Hello World\n"; }};
decltype(f1) f3{};
optional_function f4{[](int a, const int& b) -> int const& {
std::cout << a + b << '\n';
return b;
}};
optional_function f5{foo};
auto res1 = f1(9);
auto res2 = f2();
auto res3 = f3(9);
int b = 5;
auto res4 = f4(1, b);
auto res5 = f5();
std::cout << std::boolalpha;
std::cout << "f1 已执行: " << res1.has_value() << ". 结果: " << *res1
<< '\n';
std::cout << "f2 已执行: " << res2.has_value() << '\n';
std::cout << "f3 已执行: " << res3.has_value() << '\n';
std::cout << "f4 已执行: " << res4.has_value() << ". 结果: " << *res4
<< '\n';
std::cout << "f5 已执行: " << res5.has_value() << '\n';
}
英文:
The standard library doesn't have anything like that, but you can build one yourself:
#include <functional>
#include <iostream>
#include <optional>
template <typename T>
class optional_function {
private:
std::optional<T> func;
public:
optional_function(T f) : func{std::move(f)} {}
optional_function() = default;
template <typename... Args>
auto operator()(Args&&... args) const {
using func_invoke_type = decltype((*func)(std::forward<Args>(args)...));
constexpr bool func_invoke_type_is_void = std::is_same_v<void, func_invoke_type>;
using optional_result_type = std::optional<
std::conditional_t<
func_invoke_type_is_void, // Can't have a std::optional<void>
char,
std::conditional_t<
std::is_reference_v<func_invoke_type>, // Can't have a std::optional<T&>
std::reference_wrapper<std::remove_reference_t<func_invoke_type>>,
func_invoke_type
>
>
>;
if (func) {
if constexpr (!func_invoke_type_is_void) {
return optional_result_type{(*func)(std::forward<Args>(args)...)};
} else {
(*func)(std::forward<Args>(args)...);
return optional_result_type{ '\0' }; // can't return void{} '
}
}
return optional_result_type{};
}
};
// Test it
void foo() {}
int main() {
optional_function f1{[](int i) { return i * i; }};
optional_function f2{[] { std::cout << "Hello World\n"; }};
decltype(f1) f3{};
optional_function f4{[](int a, const int& b) -> int const& {
std::cout << a + b << '\n';
return b;
}};
optional_function f5{foo};
auto res1 = f1(9);
auto res2 = f2();
auto res3 = f3(9);
int b = 5;
auto res4 = f4(1, b);
auto res5 = f5();
std::cout << std::boolalpha;
std::cout << "f1 is executed: " << res1.has_value() << ". result: " << *res1
<< '\n';
std::cout << "f2 is executed: " << res2.has_value() << '\n';
std::cout << "f3 is executed: " << res3.has_value() << '\n';
std::cout << "f4 is executed: " << res4.has_value() << ". result: " << *res4
<< '\n';
std::cout << "f5 is executed: " << res5.has_value() << '\n';
}
答案3
得分: 0
目前在C++标准库中没有这样的东西。
英文:
No, there is currently no such thing in the C++ Standard Library.
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论