Reference: here.

The return of overloaded operator should be a reference, otherwise return-by-code will create a (temporary) rvalue that cannot be passed to the next operation f2 by non-const reference. i.e., rvalue cannot be non-const referenced.

#include <vector>
#include <iostream>
#include <functional>

template<typename T, typename FN>
requires std::invocable<FN, T&>  // diff std::invocable?
std::vector<T>& operator| (std::vector<T>& vec, FN fn) noexcept {
    for(auto& e: vec) {
        fn(e);
    }
    return vec;
}

int main(){
    std::vector v{1, 2, 3};
    auto f1 = [](int& i) {i *= i; };
    std::function f2 {[](const int& i) {std::cout << i << ' '; } };
    v | f1 | f2;
}```