“是否有类似给定的 std::optional_function?”

huangapple go评论74阅读模式
英文:

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&lt;typename F, typename... Args, typename R = std::invoke_result_t&lt;F, Args&amp;&amp;...&gt;&gt;
auto optionally_call(F&amp;&amp; f, Args&amp;&amp;... args) -&gt; std::optional&lt;R&gt; {
    return f ? R(std::forward&lt;F&gt;(f)(std::forward&lt;Args&gt;(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 &lt;functional&gt;
#include &lt;iostream&gt;
#include &lt;optional&gt;

template &lt;typename T&gt;
class optional_function {
 private:
  std::optional&lt;T&gt; func;

 public:
  optional_function(T f) : func{std::move(f)} {}

  optional_function() = default;

  template &lt;typename... Args&gt;
  auto operator()(Args&amp;&amp;... args) const {
    using func_invoke_type = decltype((*func)(std::forward&lt;Args&gt;(args)...));

    constexpr bool func_invoke_type_is_void = std::is_same_v&lt;void, func_invoke_type&gt;;

    using optional_result_type = std::optional&lt;
        std::conditional_t&lt;
            func_invoke_type_is_void, // Can&#39;t have a std::optional&lt;void&gt;
            char,
            std::conditional_t&lt;
                std::is_reference_v&lt;func_invoke_type&gt;, // Can&#39;t have a std::optional&lt;T&amp;&gt;
                std::reference_wrapper&lt;std::remove_reference_t&lt;func_invoke_type&gt;&gt;,
                func_invoke_type
            &gt;
        &gt;
    &gt;;

    if (func) {
      if constexpr (!func_invoke_type_is_void) {
        return optional_result_type{(*func)(std::forward&lt;Args&gt;(args)...)};
      } else {
        (*func)(std::forward&lt;Args&gt;(args)...);
        return optional_result_type{ &#39;\0&#39; }; //  can&#39;t return void{} &#39;
      }
    }
    return optional_result_type{};
  }
};

// Test it

void foo() {}

int main() {
  optional_function f1{[](int i) { return i * i; }};
  optional_function f2{[] { std::cout &lt;&lt; &quot;Hello World\n&quot;; }};
  decltype(f1) f3{};
  optional_function f4{[](int a, const int&amp; b) -&gt; int const&amp; {
    std::cout &lt;&lt; a + b &lt;&lt; &#39;\n&#39;;
    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 &lt;&lt; std::boolalpha;
  std::cout &lt;&lt; &quot;f1 is executed: &quot; &lt;&lt; res1.has_value() &lt;&lt; &quot;. result: &quot; &lt;&lt; *res1
            &lt;&lt; &#39;\n&#39;;
  std::cout &lt;&lt; &quot;f2 is executed: &quot; &lt;&lt; res2.has_value() &lt;&lt; &#39;\n&#39;;
  std::cout &lt;&lt; &quot;f3 is executed: &quot; &lt;&lt; res3.has_value() &lt;&lt; &#39;\n&#39;;
  std::cout &lt;&lt; &quot;f4 is executed: &quot; &lt;&lt; res4.has_value() &lt;&lt; &quot;. result: &quot; &lt;&lt; *res4
            &lt;&lt; &#39;\n&#39;;
  std::cout &lt;&lt; &quot;f5 is executed: &quot; &lt;&lt; res5.has_value() &lt;&lt; &#39;\n&#39;;
}

答案3

得分: 0

目前在C++标准库中没有这样的东西。

英文:

No, there is currently no such thing in the C++ Standard Library.

huangapple
  • 本文由 发表于 2020年1月6日 22:54:29
  • 转载请务必保留本文链接:https://go.coder-hub.com/59614228.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定