【问题标题】:C++14 Lambda - Conditionally Capture by Reference or ValueC++14 Lambda - 通过引用或值有条件地捕获
【发布时间】:2014-11-26 15:29:17
【问题描述】:

是否可以根据编译时信息有条件地选择 lambda 的捕获方法?比如……

auto monad = [](auto && captive) {
    return [(?)captive = std::forward<decltype(captive)>(captive)](auto && a) {
        return 1;
    };
};

如果decltype(captive)std::reference_wrapper,我希望通过引用捕获,其他所有内容都按值捕获。

【问题讨论】:

  • reference_wrapper 的重点不是就像引用一样,只是它可以按值传递而没有问题吗?那么为什么 that 类型是您要通过引用捕获的类型?仅按价值捕获所有内容有什么问题?
  • @hvd 我不想通过引用捕获reference_wrapper,我想通过引用捕获它持有的引用。引用包装器最好是一个 like 引用,但由于调用运算符(又名“.”运算符)不能重载,所以它最终会非常悲惨地失败。
  • 感谢您的澄清。这更有意义。所以你不想通过引用捕获captive,如果它是reference_wrapper,你想通过引用捕获captive.get(),对吧?
  • @hvd,是的,完全正确。我应该编辑这个问题,因为我没有说得很清楚
  • @pat 被称为“operator-dot”,当我阅读“调用运算符”时,我以为你的意思是函数调用运算符,operator()

标签: c++ lambda c++14


【解决方案1】:

Lambda 捕获类型不能由依赖于模板的名称控制。

但是,您可以通过将创建内部 lambda 委托给重载函数来实现所需的效果:

template<class T>
auto make_monad(T&& arg) {
    return [captive = std::forward<T>(arg)](auto&& a) {
        std::cout << __PRETTY_FUNCTION__ << " " << a << '\n';
        return 1;
    };
}

template<class T>
auto make_monad(std::reference_wrapper<T> arg) {
    return [&captive = static_cast<T&>(arg)](auto&& a) {
        std::cout << __PRETTY_FUNCTION__ << " " << a << '\n';
        return 1;
    };
}

int main() {
    auto monad = [](auto&& captive) {
        return make_monad(std::forward<decltype(captive)>(captive));
    };

    int n = 1;
    monad(1)(1);
    monad(n)(2);
    monad(std::ref(n))(3);
}

输出:

make_monad(T&&)::<lambda(auto:1&&)> [with auto:1 = int; T = int] 1
make_monad(T&&)::<lambda(auto:1&&)> [with auto:1 = int; T = int&] 2
make_monad(std::reference_wrapper<_Tp>)::<lambda(auto:2&&)> [with auto:2 = int; T = int] 3

我不想通过引用来捕获reference_wrapper,我想通过引用来捕获它所持有的引用。引用包装器最好是一个 like 引用,但由于调用运算符(又名“.”运算符)不能被重载,它最终会非常悲惨地失败。

在这种情况下,您无需更改std::reference_wrapper&lt;T&gt; 的捕获类型。相反,您可能希望像任何其他类型的参数一样按值捕获它,并在使用站点首先打开参数:

template<class T> T& unwrap(T& t) { return t; }
template<class T> T& unwrap(std::reference_wrapper<T> t) { return t; }

auto monad = [](auto && captive) {
    return [captive](auto && a) {            // <--- Capture by value.
        auto& captive_ref = unwrap(captive); // <--- Unwrap before usage.
        return 1;
    };
};

【讨论】:

  • 感谢您的回复!我知道有很多老式的方法可以做到这一点,我只是希望 C++14 能够在解决方案中提供一些简洁性。
  • @Casey 最有可能的是,最初的问题是想通过引用来捕获包装器。
【解决方案2】:

它不回答你的问题,而是你的评论,如何使用operator .

您可以添加这两个重载:

template <typename T>
T& get_reference_object(T&& t) { return t; }

template <typename T>
T& get_reference_object(std::reference_wrapper<T> t) { return t.get(); }

然后你可以在你的 lambda 中使用get_reference_object(arg).foo

auto monad = [](auto && captive) {
    return [captive = captive](auto&& a) { return get_reference_object(captive).foo(a); };
};

Live example.

【讨论】:

    猜你喜欢
    • 2016-02-08
    • 2019-08-28
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2018-01-22
    • 2016-01-30
    相关资源
    最近更新 更多