捕获此内容并将 lambda 与 fmt::join 结合转发到另一个 lambda

问题描述 投票:0回答:1

我之前问过这个问题。现在我已将其扩展到以下示例中所示的实际用例(带有链接here):

#include <ranges>
#include <map>
#include <functional>
#include <fmt/format.h>
#include <iostream>

template<typename RANGE, typename LAM>
auto foo(RANGE&& range, LAM&& lambda) {
    return fmt::join(range | std::views::transform( 
                [lam = std::forward<LAM>(lambda)](const auto& element) { return lam(element); }), ",");
}


class boo {
public:
    int add {2};
    
    void call_foo(){
        std::vector<int> d { 2,3,4,5,6};
        auto f = fmt::format("{}", foo(d, [this](int i) { 
            std::cout << add << std::endl;
            return add + i; 
            }));
        std::cout << f << std::endl;
    };
};

int main() {
    std::cout << "HELLO" << std::endl;
    boo b;
    b.call_foo();
}

这再次导致

std::cout << add << std::endl;
处出现段错误。由于它在
fmt::join
内部没有
foo
的情况下工作,我怀疑
fmt::joi
n 不能很好地应对我所得到的。 关于问题是什么以及如何解决的任何想法

c++ lambda c++20 std-ranges fmt
1个回答
0
投票

那是因为这个坏了:

template<typename RANGE, typename LAM>
auto foo(RANGE&& range, LAM&& lambda) {
    return fmt::join(range | std::views::transform( 
                [lam = std::forward<LAM>(lambda)](const auto& element) { return lam(element); }), ",");
}

C++20(以及之前的 range-v3)具有

view
的广义概念,它不仅仅是迭代器/哨兵对。它也可以有任意状态。对于 views::transform
,该状态包括函数对象(当然还有底层的 
view
)。

问题是,

fmt::join

 - 虽然它
看起来像其他系列机械,但实际上并非如此。它只是做这个

template <typename Range> auto join(Range&& range, string_view sep) -> join_view<decltype(detail::adl::adlbegin(range)), decltype(detail::adl::adlend(range))> { return join(detail::adl::adlbegin(range), detail::adl::adlend(range), sep); }
也就是说 - 它只是从范围中取出 

begin()

end()
 并存储它们,扔掉范围的其余部分。

这对于某些范围(特别是

借用范围)来说是可以的,但不是全部。具有状态转换的 views::transform

(如您此处所示)不是借用的范围 - 您
需要在某处保留该可调用对象,而这在这里不会发生。因此出现了问题。

值得注意的是,这只是一个问题,因为

views::transform

实际上在使用之前就被破坏了。如果您在完整的管道构造中使用 
fmt::join
 ,它会工作得很好:

auto f = fmt::format("{}", fmt::join(d | std::views::transform([this](int i) { std::cout << add << std::endl; return add + i; }), ","));
因为临时 

transform

 直到完整表达式结束(即所有格式化发生之后)才会被销毁。

一般来说,最好只使用这样的

fmt::join

。理想情况下,我们只需扩展范围格式说明符以包含分隔符,这样我们就完全不需要 
fmt::join

© www.soinside.com 2019 - 2024. All rights reserved.