9

I have read many posts about variadic templates and std::bind but I think I am still not understanding how they work together. I think my concepts are a little hazy when it comes to using variadic templates, what std::bind is used for and how they all tie together.

In the following code my lambda uses the dot operator with objects of type TestClass but even when I pass in objects of type std::ref they still work. How is this exactly? How does the implicit conversion happen?

#include <iostream>
using std::cout;
using std::endl;
#include <functional>
#include <utility>
using std::forward;

class TestClass {
public:
    TestClass(const TestClass& other) {
        this->integer = other.integer;
        cout << "Copy constructed" << endl;
    }
    TestClass() : integer(0) {
        cout << "Default constructed" << endl;
    }
    TestClass(TestClass&& other) {
        cout << "Move constructed" << endl;
        this->integer = other.integer;
    }

    int integer;
};

template <typename FunctionType, typename ...Args>
void my_function(FunctionType function, Args&&... args) {
    cout << "in function" << endl;
    auto bound_function = std::bind(function, args...);
    bound_function();
}

int main() {

    auto my_lambda = [](const auto& one, const auto& two) {
        cout << one.integer << two.integer << endl;
    };

    TestClass test1;
    TestClass test2;
    my_function(my_lambda, std::ref(test1), std::ref(test2));

    return 0;
}

More specifically, I pass in two instances of a reference_wrapper with the two TestClass objects test1 and test2, but when I pass them to the lambda the . operator works magically. I would expect that you have use the ::get() function in the reference_wrapper to make this work but the call to the .integer data member works..

Curious
  • 20,870
  • 8
  • 61
  • 146

2 Answers2

5

The reference unwrapping is performed by the result of std::bind():

If the argument is of type std::reference_wrapper<T> (for example, std::ref or std::cref was used in the initial call to bind), then the reference T& stored in the bound argument is passed to the invocable object.

Corresponding standardese can be found in N4140 draft, [func.bind.bind]/10.

Anton Savin
  • 40,838
  • 8
  • 54
  • 90
  • Wow! That is so magical. Also could you suggest somewhere I could learn more about these std::bind and variadic templates from? Because currently they seem to be like magic to me and I can't really program when things seem like magic... – Curious Feb 28 '16 at 07:59
  • @Curious [The Definitive C++ Book Guide and List](http://stackoverflow.com/q/388242/3959454) – Anton Savin Feb 28 '16 at 08:00
  • 2
    @Curious: it's worth mentioning you should probably study bind and variadic templates seperately. They don't have any unusual interactions, bind acts exactly the same with or without variadics, and variadics act exactly the same with or without bind. As such, it's less of a head-overload to study them seperately. – Mooing Duck Feb 28 '16 at 08:16
0

It is important to note that with std::bind;

The arguments to bind are copied or moved, and are never passed by reference unless wrapped in std::ref or std::cref.

The "passed by reference" above is achieved because std::ref provides a result of std::reference_wrapper that is a value type that "wraps" the reference provided.

std::reference_wrapper is a class template that wraps a reference in a copyable, assignable object. It is frequently used as a mechanism to store references inside standard containers (like std::vector) which cannot normally hold references.

By way of an example of what bind's unwrapping of the reference does (without the bind);

#include <iostream>
#include <utility>
#include <functional>

int main()
{
    using namespace std;
    int a = 1;
    auto b = std::ref(a);
    int& c = b;
    cout << a << " " << b << " " << c << " " << endl; // prints 1 1 1
    c = 2;
    cout << a << " " << b << " " << c << " " << endl; // prints 2 2 2
}

Demo code.

Niall
  • 30,036
  • 10
  • 99
  • 142