可调用的完美转发

Perfect forwarding of a callable

我想出了以下代码来将 R()-like 转换为 void()-like 可调用对象:

#include <utility>

template<class Callable>
auto discardable(Callable&& callable)
{ return [&]() { (void) std::forward<Callable>(callable)(); }; }
//        ^-- is it ok?

int main()
{
    auto f = discardable([n=42]() mutable { return n--; });
    f();
}

我担心被引用捕获。

  1. 定义明确吗?
  2. 我能保证 callable 在其生命周期结束后永远不会被复制和使用吗?

这是标记为 C++14,但适用于以下所有标准。

您的程序是 UB,因为您使用捕获的 lambda 的悬空引用。

那么对于,你可以使用

template<class Callable>
auto discardable(Callable&& callable)
{
    return [f = std::conditional_t<
             std::is_lvalue_reference<Callable>::value,
             std::reference_wrapper<std::remove_reference_t<Callable>>,
             Callable>{std::forward<Callable>(callable)}]
    { 
        std::forward<Callable>(f)(); 
    };
}

它移动构造临时 lambda。

由于 callable 可以是一个 xvalue,它有可能在 lambda 捕获之前被销毁,因此在捕获中留下一个悬空引用。为防止这种情况,如果参数是右值,则需要复制它。

一个工作示例:

template<class Callable>
auto discardable(Callable&& callable) { // This one makes a copy of the temporary.
    return [callable = std::move(callable)]() mutable {
        static_cast<void>(static_cast<Callable&&>(callable)());
    };
}

template<class Callable>
auto discardable(Callable& callable) {
    return [&callable]() mutable {
        static_cast<void>(callable());
    };
}

如果 callable 是左值引用但它的生命周期范围小于 discardable 返回的 lambda 捕获的范围,您仍然会面临生命周期问题。因此,始终移动或复制 callable.

可能是最安全和最简单的方法

附带说明一下,尽管有新的专用实用程序可以完美转发函数对象的值类别,例如 std::apply, the standard library algorithms 总是通过按值接受函数对象来复制它们。因此,如果同时重载 operator()()&operator()()&&,标准库将始终使用 operator()()&

Lambda 是带有 operator() 的匿名结构,捕获列表是一种指定其成员类型的奇特方式。通过引用捕获真的就像它听起来的那样:你有引用成员。不难看出参考悬挂。

在这种情况下,您特别不想完美转发:根据参数是左值还是右值引用,您有不同的语义。

template<class Callable>
auto discardable(Callable& callable)
{
    return [&]() mutable { (void) callable(); };
}

template<class Callable>
auto discardable(Callable&& callable)
{
    return [callable = std::forward<Callable>(callable)]() mutable {  // move, don't copy
        (void) std::move(callable)();  // If you want rvalue semantics
    };
}