83

Can somebody give a simple example which demonstrates the functionality of std::ref? I mean an example in which some other constructs (like tuples, or data type templates) are used only if it is impossible to explain std::ref without them.

I found two questions about std::ref here and here. But in the first one it goes about a bug in a compiler and in the second one, examples of use of std::ref do not contain std::ref and they involve tuples and data type templates which make understanding of these examples complex.

Community
  • 1
  • 1
Roman
  • 124,451
  • 167
  • 349
  • 456

4 Answers4

99

You should think of using std::ref when a function:

  • takes a template parameter by value
  • copies/moves a template parameter, such as std::bind or the constructor for std::thread.

std::ref creates a copyable value type that behaves like a reference.

This example makes demonstrable use of std::ref.

#include <iostream>
#include <functional>
#include <thread>

void increment( int &x )
{
  ++x;
}

int main()
{
  int i = 0;

  // Here, we bind increment to a COPY of i...
  std::bind( increment, i ) ();
  //                        ^^ (...and invoke the resulting function object)

  // i is still 0, because the copy was incremented.
  std::cout << i << std::endl;

  // Now, we bind increment to std::ref(i)
  std::bind( increment, std::ref(i) ) ();
  // i has now been incremented.
  std::cout << i << std::endl;

  // The same applies for std::thread
  std::thread( increment, std::ref(i) ).join();
  std::cout << i << std::endl;
}

Output:

0
1
2
Drew Dormann
  • 59,987
  • 13
  • 123
  • 180
  • 5
    It's probably worth pointing out that `std::ref` is rebindable, unlike a conventional reference, which can matter if you're feeding one into a template function. See http://stackoverflow.com/questions/37101301/is-this-stdref-behaviour-logical for an example. – Michael Anderson May 09 '16 at 03:43
16
void PrintNumber(int i) {...}

int n = 4;
std::function<void()> print1 = std::bind(&PrintNumber, n);
std::function<void()> print2 = std::bind(&PrintNumber, std::ref(n));

n = 5;

print1(); //prints 4
print2(); //prints 5

std::ref is mainly used to encapsulate references when using std::bind (but other uses are possible of course).

sbabbi
  • 11,070
  • 2
  • 29
  • 57
10

Another place where you may need std::ref is when passing objects to threads where you want each thread to operate on the single object and not a copy of the object.

int main(){
BoundedBuffer buffer(200);

std::thread c1(consumer, 0, std::ref(buffer));
std::thread c2(consumer, 1, std::ref(buffer));
std::thread c3(consumer, 2, std::ref(buffer));
std::thread p1(producer, 0, std::ref(buffer));
std::thread p2(producer, 1, std::ref(buffer));

c1.join();
c2.join();
c3.join();
p1.join();
p2.join();

return 0; }

where you wish various functions running in various threads to share a single buffer object. This example was stolen from this excellent tutorial ( C++11 Concurrency Tutorial - Part 3: Advanced locking and condition variables (Baptiste Wicht) ) (hope I did the attribution correctly)

bd2357
  • 704
  • 9
  • 17
  • The reason to use `std::ref` here, is constructor of `thread` requires a parameter type of `std::reference_wrapper` – Deqing May 06 '15 at 03:45
  • It is not the main point here but this has possible data race though. – rjhcnf Oct 26 '20 at 19:12
0

// Producer Consumer Problem

#include <iostream>
#include <thread>
#include <mutex>
#include <deque>
#include <condition_variable>
using namespace std;

class Buffer {

    std::mutex m;
    std::condition_variable cv;
    std::deque<int> queue;
    const unsigned long size = 1000;

    public:
    void addNum(int num) {
        std::unique_lock<std::mutex> lock(m);
        cv.wait(lock, [this]() { return queue.size() <= size; });
        queue.push_back(num);
        cout << "Pushed " << num << endl;
        lock.unlock();
        cv.notify_all();
    }
    int removeNum() {
        std::unique_lock<std::mutex> lock(m);
        cv.wait(lock, [this]() { return queue.size()>0; });
        int num = queue.back();
        queue.pop_back();
        cout << "Poped " << num << endl;
        lock.unlock();
        cv.notify_all();
        return num;
    }

};

void producer(int val, Buffer& buf) {
    for(int i=0; i<val; ++i){
        buf.addNum(i);
    }
}

void consumer(int val, Buffer& buf){
    for(int i=0; i<val; ++i){
        buf.removeNum();
    }
}

int main() {
    Buffer b;
    std::thread t1(producer, 1000, std::ref(b));
    std::thread t2(consumer, 1000, std::ref(b));

    t1.join();
    t2.join();
    return 0;
}

Just another use of std::ref in main while passing Buffer object as reference in producer and consumer. If std::ref not used then this code will not compile.

Rupesh Yadav.
  • 894
  • 2
  • 10
  • 24