Search code examples
c++referencepass-by-referencehigher-order-functionspass-by-value

Why does my std::ref not work as expected?


std::ref gives you a lvalue-reference to something. This reference is wrapped into an object that you can then pass around either by reference or by value.

The expected behavior of the code below is that it prints i is 2, but it prints i is 1. Why is that?

Why do I have this expectation? Because I am passing tmp via std::ref to wrapper. In wrapper the reference is then captured by value. I would have assumed that, since I am using std::ref this value is now still a reference to tmp. I am changing tmp and would expect that f reflects that change.

Play with the code here.

#include <iostream>
#include <functional>

template<typename F>
auto wrapper(int i, F func) {
    return [=]() { return func(i); };
}

void f(int i) {
    std::cout << "i is " << i << '\n';
}

int main() {
    int tmp = 1;
    auto func = wrapper(std::ref(tmp), f);
    tmp = 2;
    func();
}

Solution

  • The reason this does not work is because your wrapper function takes an int as argument.

    std::ref returns a std::reference_wrapper. When you pass it to a function that wants an int you will get an implicit conversion and you arre no longer working with a reference.

    If you change your function signature to use a std::reference_wrapper it will give the intended result.

    #include <iostream>
    #include <functional>
    
    template<typename F>
    auto wrapper(std::reference_wrapper<int> i, F func) {
        return [=]() { return func(i); };
    }
    
    void f(int i) {
        std::cout << "i is " << i << '\n';
    }
    
    int main() {
        int tmp = 1;
        auto func = wrapper(std::ref(tmp), f);
        tmp = 2;
        func();
    }