代码之家  ›  专栏  ›  技术社区  ›  YSC

呼叫的完美转发

  •  17
  • YSC  · 技术社区  · 5 年前

    我想出了以下代码来转换 R() 像进入一个 void() -如可调用:

    #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,但适用于所有以下标准。

    3 回复  |  直到 5 年前
        1
  •  12
  •   Passer By    5 年前

    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
        };
    }
    
        2
  •  7
  •   Maxim Egorushkin    5 年前

    自从 callable 可以是XValue,它有可能在lambda捕获之前被销毁,因此在捕获中留下悬空的引用。为了防止这种情况发生,如果参数是R值,则需要复制它。

    一个工作示例:

    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());
        };
    }
    

    如果 可赎回的 是L值引用,但其生存期范围小于由返回的lambda捕获的生存期范围。 discardable . 因此,移动或复制可能是最安全和最容易的 可赎回的 .

    另一方面,尽管有一些新的专门的实用程序可以完善功能对象的价值类别,比如 std::apply , the standard library algorithms 总是通过接受值来复制函数对象。如果一个超负荷的话 operator()()& operator()()&& 标准库总是使用 operator()()& .

        3
  •  5
  •   Jarod42    5 年前

    当使用捕获lambda的悬空引用时,程序是ub。

    所以 perfect forward capture in 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)(); 
        };
    }
    

    它move构造临时lambda。