Search code examples
c++std-rangesc++23

Why returning a "modifiable" ranges view from a function causes a compile error but the same code outside the function doesn't


Consider this simplified example:

using MyVar = std::variant<int, float>;
using Container = std::vector<MyVar>;

static Container myContainer;

template <typename T> auto &JustOneType()
{
    return myContainer |
           std::ranges::views::filter([](auto &var) { return std::holds_alternative<T>(var); }) |
           std::ranges::views::transform([](auto &var) -> auto & { return std::get<T>(var); });
}

void MyFun()
{

    auto x = // This is fine
        myContainer |
        std::ranges::views::filter([](auto &var) { return std::holds_alternative<int>(var); }) |
        std::ranges::views::transform([](auto &var) -> auto & { return std::get<int>(var); });

    auto y = JustOneType<int>(); // This causes a compile error
}

Why does the line auto y cause the following error?

error: cannot bind non-const lvalue reference of type.... to an rvalue of type...

But on the other hand, the line auto x is completely fine.


Solution

  • You cannot return a reference to a prvalue.

    Consider these two functions

    auto& foo() {
        int i = 1;
        return i;  // will dangle
    }
    auto& bar() {
        return 1;  // does not compile
    }
    

    While foo() will compile and return a reference to an int, the reference will dangle, as it is a reference to the temporary i. bar() on the other hand will not compile, because how could you take a reference to 1 (a prvalue)? You probably want auto func() instead of auto& func().