::std::function 的 constexpr 版本
constexpr version of ::std::function
我正在寻找可在 constexpr 中使用的 ::std::function。
用例:我有一个函数将函数指针作为参数,第二个函数将 lambda 传递给第一个函数。两者在编译时都是完全可执行的,所以我想对它们进行 constexpr。
例如:
template <class _Type>
class ConstexprFunctionPtr
{
private:
using Type = typename ::std::decay<_Type>::type;
const Type function;
public:
constexpr inline
ConstexprFunctionPtr(const Type f)
: function(f)
{ }
template <typename... Types>
constexpr inline
auto
operator() (Types... args)
const {
return function(args... );
}
};
constexpr inline
void
test()
{
ConstexprFunctionPtr<int(int)> test([](int i) -> int {
return i + 1;
});
int i = test(100);
ConstexprFunctionPtr<int(int)> test2([=](int i) -> int {
return i + 1;
});
i = test2(1000);
}
但是,这只有效,因为我将 lambda 转换为函数指针,并且当然无法捕获 lambda,如第二个示例所示。谁能给我一些关于如何通过捕获 lambda 来做到这一点的指示?
这将演示用例:
constexpr
void
walkOverObjects(ObjectList d, ConstexprFunctionPtr<void(Object)> fun) {
// for i in d, execute fun
}
constexpr
void
searchObjectX(ObjectList d) {
walkOverObjects(d, /*lambda that searches X*/);
}
谢谢,
杰克
更新:
感谢您指出 C++20 解决方案,但是,我想要一个在 C++14
下工作的解决方案
I am in search of a ::std::function usable in constexpr
到此为止。不可能。 std::function
是一个多态包装函数。无状态 lambda、有状态 lambda、函子、函数指针、函数引用——所有这些都可以构建一个有效的 std::function
,可以在运行时更改。因此,使编译时间等效只是浪费时间。
如果你只是想要一个编译时泛型函数参数,你可以只使用模板
template<class functor_type>
class my_generic_function_consumer_class{
using decayed_function_type = typename std::decay_t<functor_type>;
decayed_function_type m_functor;
};
在您的相关代码中,只需接受一个通用仿函数,并使用 static_assert
:
对其进行验证
template<class function_type>
constexpr void walkOverObjects(ObjectList d, function_type&& fun) {
static_assert(std::is_constructible_v<std::function<void(ObjectList), function_type>>,
"function_type given to walkOverObjects is invalid.");
}
C++20 发生了很多变化 --- 最重要的是,您现在可以在 constexpr
上下文中使用动态内存和虚函数。这使得构建 std::function 的 constexpr 版本成为可能。这是一个概念验证(它很长并且没有复制或移动构造函数,所以请不要按原样使用它)。它在 clang 10 下编译,运行 代码 here。我没有在其他编译器下尝试过,值得注意的是 none 的主要编译器目前都声称拥有 C++-20 的完整实现。
#include <type_traits>
#include <utility>
#include <functional>
template<typename Ret, typename... Args> struct _function{
constexpr virtual Ret operator()(Args...) const = 0;
constexpr virtual ~_function() = default;
};
template<typename F, typename Ret, typename... Args> struct _function_impl : public _function<Ret,Args...>{
F f;
constexpr Ret operator()(Args... args) const override {
return f(std::forward<Args>(args)...);
}
constexpr _function_impl(F&& f):f(f){}
};
template<typename > struct function;
template<typename Ret, typename... Args> struct function<Ret (Args...)>{
_function<Ret,Args...> *real_f{nullptr};
constexpr Ret operator()(Args... args) const {
return real_f->operator()(std::forward<Args>(args)...);
}
constexpr ~function(){
if (real_f) delete real_f;
}
template<typename F>
constexpr function(F&& f):real_f(new _function_impl<std::decay_t<F>,Ret,Args...>(std::move(f))){}
};
template<typename Ret, typename... Args>
constexpr Ret call_f_2(const function<Ret(Args...)> &f, Args... a){
return f(std::forward<Args>(a)...);
}
template<typename F, typename... Args>
constexpr decltype(auto) call_f(F && f, Args&&... a){
using Ret = std::invoke_result_t<std::decay_t<F>,Args...>;
function<Ret(Args...)> f2 = std::move(f);
return call_f_2<Ret,Args...>(f2,a...);
}
int main(){
constexpr int c = 3;
constexpr int i = call_f([c](int j) constexpr {return c + j;},4);
return i;
}
我正在寻找可在 constexpr 中使用的 ::std::function。 用例:我有一个函数将函数指针作为参数,第二个函数将 lambda 传递给第一个函数。两者在编译时都是完全可执行的,所以我想对它们进行 constexpr。 例如:
template <class _Type>
class ConstexprFunctionPtr
{
private:
using Type = typename ::std::decay<_Type>::type;
const Type function;
public:
constexpr inline
ConstexprFunctionPtr(const Type f)
: function(f)
{ }
template <typename... Types>
constexpr inline
auto
operator() (Types... args)
const {
return function(args... );
}
};
constexpr inline
void
test()
{
ConstexprFunctionPtr<int(int)> test([](int i) -> int {
return i + 1;
});
int i = test(100);
ConstexprFunctionPtr<int(int)> test2([=](int i) -> int {
return i + 1;
});
i = test2(1000);
}
但是,这只有效,因为我将 lambda 转换为函数指针,并且当然无法捕获 lambda,如第二个示例所示。谁能给我一些关于如何通过捕获 lambda 来做到这一点的指示?
这将演示用例:
constexpr
void
walkOverObjects(ObjectList d, ConstexprFunctionPtr<void(Object)> fun) {
// for i in d, execute fun
}
constexpr
void
searchObjectX(ObjectList d) {
walkOverObjects(d, /*lambda that searches X*/);
}
谢谢, 杰克
更新: 感谢您指出 C++20 解决方案,但是,我想要一个在 C++14
下工作的解决方案I am in search of a ::std::function usable in constexpr
到此为止。不可能。 std::function
是一个多态包装函数。无状态 lambda、有状态 lambda、函子、函数指针、函数引用——所有这些都可以构建一个有效的 std::function
,可以在运行时更改。因此,使编译时间等效只是浪费时间。
如果你只是想要一个编译时泛型函数参数,你可以只使用模板
template<class functor_type>
class my_generic_function_consumer_class{
using decayed_function_type = typename std::decay_t<functor_type>;
decayed_function_type m_functor;
};
在您的相关代码中,只需接受一个通用仿函数,并使用 static_assert
:
template<class function_type>
constexpr void walkOverObjects(ObjectList d, function_type&& fun) {
static_assert(std::is_constructible_v<std::function<void(ObjectList), function_type>>,
"function_type given to walkOverObjects is invalid.");
}
C++20 发生了很多变化 --- 最重要的是,您现在可以在 constexpr
上下文中使用动态内存和虚函数。这使得构建 std::function 的 constexpr 版本成为可能。这是一个概念验证(它很长并且没有复制或移动构造函数,所以请不要按原样使用它)。它在 clang 10 下编译,运行 代码 here。我没有在其他编译器下尝试过,值得注意的是 none 的主要编译器目前都声称拥有 C++-20 的完整实现。
#include <type_traits>
#include <utility>
#include <functional>
template<typename Ret, typename... Args> struct _function{
constexpr virtual Ret operator()(Args...) const = 0;
constexpr virtual ~_function() = default;
};
template<typename F, typename Ret, typename... Args> struct _function_impl : public _function<Ret,Args...>{
F f;
constexpr Ret operator()(Args... args) const override {
return f(std::forward<Args>(args)...);
}
constexpr _function_impl(F&& f):f(f){}
};
template<typename > struct function;
template<typename Ret, typename... Args> struct function<Ret (Args...)>{
_function<Ret,Args...> *real_f{nullptr};
constexpr Ret operator()(Args... args) const {
return real_f->operator()(std::forward<Args>(args)...);
}
constexpr ~function(){
if (real_f) delete real_f;
}
template<typename F>
constexpr function(F&& f):real_f(new _function_impl<std::decay_t<F>,Ret,Args...>(std::move(f))){}
};
template<typename Ret, typename... Args>
constexpr Ret call_f_2(const function<Ret(Args...)> &f, Args... a){
return f(std::forward<Args>(a)...);
}
template<typename F, typename... Args>
constexpr decltype(auto) call_f(F && f, Args&&... a){
using Ret = std::invoke_result_t<std::decay_t<F>,Args...>;
function<Ret(Args...)> f2 = std::move(f);
return call_f_2<Ret,Args...>(f2,a...);
}
int main(){
constexpr int c = 3;
constexpr int i = call_f([c](int j) constexpr {return c + j;},4);
return i;
}