Search code examples
c++c++20boost-hanastd-ranges

Why wrapping call to function returning by value in hana::always circumvent requirements of ranges::views::join? Or maybe it doesn't?


This function, fed with any int, returns a std::vector<int> by value:

auto make = [](int){
    return std::vector<int>{1,2,3};
};

Therefore, such a thing can't work

std::vector<int> v{1,2,3};
auto z = v | std::ranges::views::transform(make)
           | std::ranges::views::join; // fails to compile

because, I understand (but correct me if I'm wrong), when the iterator wrapped in join advances, it triggers the generations of the vectors by means of make, but those temporary are already destroyed by the time the iterator in join is dereferenced.

However, the following doesn't fail:

std::vector<int> v{1,2,3};
auto z = v | std::ranges::views::transform(boost::hana::always(make(int{})))
           | std::ranges::views::join;

Why is that? What mechanism is being introduced by the use of always?

I think the following is proof that both functions, make and always(make(int{})), return a temporary. Am I making a mistake?

static_assert(!std::is_reference_v<decltype(make(int{}))>);
static_assert(!std::is_reference_v<decltype(boost::hana::always(make(int{}))(int{}))>);

Full demo.


Solution

  • There are two separate issues.

    • join_view's restriction on joining ranges of prvalue ranges is a defect in C++20 that has been corrected by P2328R1. transform(make) | join should Just Work on a standard library implementing the defect resolution (such as libstdc++ trunk).

    • hana::always returns different things depending on whether it is invoked as an lvalue or rvalue. It returns an lvalue reference when invoked as an lvalue and a prvalue when invoked as an rvalue. transform always invokes as lvalue, while your static_assert is checking the result of invoking as rvalue.