c++ - 如何在 C++20 的模板化仿函数中处理 void 返回类型?
问题描述
我已经构建了一个模板化仿函数对象,我可以使用它来管理需要递归和跨范围生存的 lambda。它不是很漂亮(它使用 void 指针和一个std::function
实例),但它适用于我需要它的用例。(如果张贴者可以保留评论它是如何不安全且非常糟糕的做法,我将不胜感激。我知道。)
但是,它有一个明显的问题:它无法处理 returnvoid
的 lambda,因为某些路径试图将返回值存储在变量中。我需要知道如何使用if constexpr
语句来检测仿函数的 lambda 的结果是否为 void,并适当地处理它。这不是一个独特的问题,但我发现的所有结果都非常过时,其中许多都使用现已折旧的result_of_t
.
任何帮助将不胜感激。
#include <iostream>
#include <string>
#include <functional>
#define uint unsigned int
//! A standardised wrapper for lambda functions, which can be stored in pointers, used recursively, keep track of external storage via a void *, and set to self destruct when no longer useful.
template <class F, bool UsesDataStorage>
class Functor {
protected:
std::function<F> m_f; //!< The lambda stored by the wrapper
void* m_data = nullptr; //!< A void pointer which will be given to the lambda if `UsesDataStorage`. Note that cleanup is delegated to the lambda; the functor instance will not handle it.
bool m_selfDestructing = true; //!< Whether the combinator will self-destruct should its lambda mark itself as no longer useful.
bool m_selfDestructTrigger = false; //!< Whether the combinator's lambda has marked itself as no longer useful.
public:
inline bool usesDataStorage() const { return UsesDataStorage; } //!< Return whether this functor is set up to give its function a `data` void-pointer, which will presumably be set to a data-structure.
inline void* getData() const { return m_data; } //!< Returns the void pointer which is passed to the lambda at each call (if the functor instance uses data storage).
inline void setData(void* data) { m_data = data; } //!< Sets the void pointer which is passed to the lambda at each call (if the functor instance uses data storage).
inline bool canSelfDestruct() const { return m_selfDestructing; } //!< Returns whether the LambdaWrapper will delete itself when instructed to by the contained lambda.
inline void triggerSelfDestruct() { m_selfDestructTrigger = true; } //!< Triggers wrapper self-deletion at the end of ruinning the lambda.
Functor(const std::function<F>& f, bool canSelfDestruct = true) :
m_f(f),
m_selfDestructing(canSelfDestruct)
{} //!< Constructor for Functor instances which DON'T use data storage. Note that the given function should always take a void pointer as the first argument, which is where a pointer to the Functor instance will be passed.
Functor(std::function<F>&& f, bool canSelfDestruct = true) :
m_f(f),
m_selfDestructing(canSelfDestruct)
{} //!< Constructor for Functor instances which DON'T use data storage. Note that the given function should always take a void pointer as the first argument, which is where a pointer to the Functor instance will be passed.
Functor(const std::function<F>& f, void* data, bool canSelfDestruct = true) :
m_f(f),
m_data(data),
m_selfDestructing(canSelfDestruct)
{} //!< Constructor for Functor instances which DO use data storage. Note that the given function should always take a void pointer as the first argument, which is where a pointer to the Functor instance will be passed, and a void * for the second argument, which is where the data storage pointer is passed.
Functor(std::function<F>&& f, void* data, bool canSelfDestruct = true) :
m_f(f),
m_data(data),
m_selfDestructing(canSelfDestruct)
{} //!< Constructor for Functor instances which DO use data storage. Note that the given function should always take a void pointer as the first argument, which is where a pointer to the Functor instance will be passed, which is where the data storage pointer is passed.
template <typename... Args>
decltype(auto) operator()(Args&&... args) {
// Avoid storing return if we can,
if (!m_selfDestructing) {
if constexpr (UsesDataStorage) {
// Pass itself to m_f, then the data storage, then the arguments.
// This should work even if the return type is void, as far as I can tell.
return m_f(this, m_data, std::forward<Args>(args)...);
}
else {
// Pass itself to m_f, then the arguments.
// This should work even if the return type is void, as far as I can tell.
return m_f(this, std::forward<Args>(args)...);
}
}
else {
if constexpr (UsesDataStorage) {
// Pass itself to m_f, then the data storage, then the arguments.
// ----- !!! -----
// The following if constexpr statement is what I can't work out how to do.
// ----- !!! -----
if constexpr (std::is_same<std::invoke_result_t<std::function<F>>, void>) {
m_f(this, m_data, std::forward<Args>(args)...);
// self-destruct if necessary, allowing lamdas to delete themselves if they know they're no longer useful.
if (m_selfDestructTrigger) { delete this; }
return;
}
else {
auto r = m_f(this, m_data, std::forward<Args>(args)...);
// self-destruct if necessary, allowing lamdas to delete themselves if they know they're no longer useful.
if (m_selfDestructTrigger) { delete this; }
return r;
}
}
else {
// Pass itself to m_f, then the arguments.
// ----- !!! -----
// The following if constexpr statement is what I can't work out how to do.
// ----- !!! -----
if constexpr (std::is_same<std::invoke_result_t<std::function<F>>, void>) {
m_f(this, std::forward<Args>(args)...);
// self-destruct if necessary, allowing lamdas to delete themselves if they know they're no longer useful.
if (m_selfDestructTrigger) { delete this; }
return;
}
else {
auto r = m_f(this, std::forward<Args>(args)...);
// self-destruct if necessary, allowing lamdas to delete themselves if they know they're no longer useful.
if (m_selfDestructTrigger) { delete this; }
return r;
}
}
}
}
};
template <class F> Functor(std::function<F>, bool)->Functor<F, false>;
template <class F> Functor(std::function<F>, void*, bool)->Functor<F, true>;
int main() {
Functor f1 = Functor(std::function([](void* self, uint val1) -> uint {
std::cout << "f1(" << val1 << ") was called." << std::endl;
return 2u * val1;
}), false);
Functor f2 = Functor(std::function([](void* self, uint val1) -> void {
std::cout << "f2(" << val1 << ") was called." << std::endl;
return;
}), false);
auto x = f1(3u); // Compiles and works.
f2(3u); // Doesn't compile.
}
解决方案
我正在寻找的行是这样的:
if constexpr (std::is_same<std::function<F>::result_type, void>::value) {}
非常感谢@NathanOliver!
推荐阅读
- elasticsearch - Nutch 索引到 ElasticSearch
- c++ - 如何 static_cast 指向 const 成员函数的指针?
- date - NOT IN SUBQUERIES SQL 编译错误:无法评估不支持的子查询类型
- ios - 在 Swift 的特定视图控制器中锁定应用程序的方向
- ios - Flutter项目M1 Macbook中更新cocoapods时出错
- c# - 在选择器上仅显示活跃的企业帐户
- sockets - TCL 原始套接字多行响应
- typescript - 为 react-native-config 配置声明模块?
- plot - Mathematica 绘图求解结果
- excel - 对象变量或未设置块变量,但我找不到问题