以lambda作为参数的约定 我正在编写一个C++应用程序,并在lambda参数上抽象了一个方法。我最初使用了以下签名: void atomically(void (*block)(T& value, bool& retry))
但是我发现不能将任意lambda作为函数指针传递——lambda必须没有捕获。我可以用以lambda作为参数的约定 我正在编写一个C++应用程序,并在lambda参数上抽象了一个方法。我最初使用了以下签名: void atomically(void (*block)(T& value, bool& retry)),c++,lambda,C++,Lambda,但是我发现不能将任意lambda作为函数指针传递——lambda必须没有捕获。我可以用 void atomically(const std::function<void (T&, bool&)>& block) 但我发现这是不可接受的,因为它允许块按值而不是按引用获取其参数,这很容易出错,调试起来也很微妙 将lambda作为参数的正确约定是什么?或者,在最终的模板解决方案中,是否有办法确保块通过引用获取其参数?我认为您不应该将lambda作为参数调用,它们将
void atomically(const std::function<void (T&, bool&)>& block)
但我发现这是不可接受的,因为它允许块
按值而不是按引用获取其参数,这很容易出错,调试起来也很微妙
将lambda作为参数的正确约定是什么?或者,在最终的模板解决方案中,是否有办法确保
块
通过引用获取其参数?我认为您不应该将lambda作为参数调用,它们将生成值-而在这种情况下,您的函数在原子上期望函数作为参数,可能您可以使用std:function来描述函数原型,正如本文所述,您可以使用以下内容:
template <typename T>
using block = std::function<void(T&, bool&)>;
模板
使用block=std::function;
避免使用传递指针并能够通过引用传递 也许这是一种愚蠢的方式,但是。。。如果你能使用C++17。。。您可以尝试使用
std::function
演绎指南
我是说。。。您可以准备一个自定义类型特征来检查std::function
是否属于请求的类型
template <typename>
struct checkFunc : public std::false_type
{ };
template <typename T>
struct checkFunc<std::function<void(T&, bool&)>> : public std::true_type
{ };
你可以核实一下
auto l1 = [](int &, bool &){};
atomically(l1);
编译,因为l1
的类型将F
作为std::function
生成与checkFunc
专门化匹配的类型,所以启用了原子(l1)
,并且
auto l2 = [](int, bool &){};
atomically(l2);
不编译,因为l2
的类型生成的F
类型为std::function
,与checkFunc
专门化不匹配,因此原子(l2)
未启用
一种可能的替代方法是atomically()
表单,可能可读性稍差,但其优点是第二个模板参数无法解释,如下所示
template <typename L>
std::enable_if_t<checkFunc<decltype(std::function{std::declval<L>()})>::value>
atomically (L const & bl)
{ }
模板
std::如果启用,则启用
原子(L常量和bl)
{ }
注意,在这两种情况下,编译时都会使用
std::function
,以决定是否启用原子()
,但在运行时不会创建std::function
对象:lambda保持lambda并作为lambda运行。您可能需要一些function\u视图
(取自)(顾名思义,没有所有权,所以不要存储它):
模板
类功能视图;
模板
类函数\u视图最终
{
私人:
使用签名类型=TReturn(无效*,目标…);
无效*\u ptr;
TReturn(*已擦除)(无效*,目标;
公众:
模板>
函数_view(T&&x)noexcept:_ptr{(void*)std::addressof(x)}
{
_已擦除\u fn=[](无效*ptr,目标…xs)->t返回{
返回(*重新解释铸件(ptr))(
标准:正向(xs);
};
}
decltype(自动)运算符()(TArgs…xs)常量
noexcept(noexcept(_擦除的_fn(_ptr,std::forward(xs)…))
{
返回_擦除_fn(_ptr,std::forward(xs)…);
}
};
然后您可以使用:
void atomically(const function_view<void (T&, bool&)>& block)
void原子(常量函数视图和块)
如评论中所述,T&
是T
的一个子类型,因此你必须更聪明,但不要太糟糕。我们可以明确要求我们想要的东西,禁止我们不想要的东西:
template<class F>
void atomically(const F& block)
{
static_assert(std::is_invocable_v<const F&, T&, bool&>, "wrong signature");
static_assert(!std::is_invocable_v<const F&, T&&, bool&>, "need to mutate value");
static_assert(!std::is_invocable_v<const F&, T&, bool&&>, "need to mutate retry");
}
模板
原子无效(常数F和块)
{
静态断言(std::is_invocable_v,“错误签名”);
静态断言(!std::is_invocable_v,“需要改变值”);
静态断言(!std::is_invocable_v,“需要变异重试”);
}
请参阅:您可能需要一个“函数”类型特征。或者,您可以使用一些
函数_view
作为源代码,为什么块
不复制其参数如此重要?您是否意识到您实际上无法强制执行它?因为,即使它通过引用获取其参数,它也可能在某个时候复制它们。@Acorn:OP想要禁止:omically([](T&T,bool&b){/**})
并强制原子地拥有([](T&T,bool&b){/**})
@luqui:注意,带有的变量在原子上无效(const std::function&block)
也没有强制执行这一点。我完全不理解这一点。我想避免std::function
,因为它在堆上分配–我不知道将其重命名为block
有什么帮助。我想这个答案是误读问题的结果。很糟糕,但它确实发生了。@luqui是的,正如您的下的粗鲁评论中提到的那样评论,我误读了这一点,对此表示歉意。我无意冒犯你。我为冒犯你表示歉意。代替C++17的CTAD,你可以检查std::function(bl)
是否格式正确(C++14)(因此使用check\u type=std::is_constructurable的模板是可构造的
)。@Jarod42-我尝试过类似的东西(通过decltype()
)但是没有成功…我将尝试std::is_constructible
@Jarod42-失败:对于std::is_constructible::value
我得到true
,其中l2
是[](int,bool&){};
(根据OP要求,这是不可接受的)是的,它的格式确实很好:-/没有真正处理您的签名约束,因为void foo(bool)
与function\u view
(和std::function
)是兼容的。确实,它没有完全处理我的约束,但在任何情况下都非常方便,谢谢
template <typename TSignature>
class function_view;
template <typename TReturn, typename... TArgs>
class function_view<TReturn(TArgs...)> final
{
private:
using signature_type = TReturn(void*, TArgs...);
void* _ptr;
TReturn (*_erased_fn)(void*, TArgs...);
public:
template <typename T, typename = std::enable_if_t<
std::is_callable<T&(TArgs...)>{} &&
!std::is_same<std::decay_t<T>, function_view>{}>>
function_view(T&& x) noexcept : _ptr{(void*)std::addressof(x)}
{
_erased_fn = [](void* ptr, TArgs... xs) -> TReturn {
return (*reinterpret_cast<std::add_pointer_t<T>>(ptr))(
std::forward<TArgs>(xs)...);
};
}
decltype(auto) operator()(TArgs... xs) const
noexcept(noexcept(_erased_fn(_ptr, std::forward<TArgs>(xs)...)))
{
return _erased_fn(_ptr, std::forward<TArgs>(xs)...);
}
};
void atomically(const function_view<void (T&, bool&)>& block)
template<class F>
void atomically(const F& block)
{
static_assert(std::is_invocable_v<const F&, T&, bool&>, "wrong signature");
static_assert(!std::is_invocable_v<const F&, T&&, bool&>, "need to mutate value");
static_assert(!std::is_invocable_v<const F&, T&, bool&&>, "need to mutate retry");
}