std::make_tuple doesn't take an rvalue reference to a T, contrary to what it seems; it takes a universal reference to a T (T&&). If universal reference is new to you, let me explain.
The definition of make_tuple looks more or less like this:
template<typename... Ts>
std::tuple<Ts...> make_tuple(Ts&&... ts){
// ...
}
But for purposes of explanation, I am going to refer to make_tuple like this:
template<typename T>
std::tuple<T> make_tuple(T&& t){
// ...
}
Using type deduction, when make_tuple is passed an rvalue (lets say an int&&), the type deduced for T is int, since make_tuple takes a T&& and it was passed an rvalue. The definition of make_tuple (with T deduced) now looks like this:
std::tuple<int> make_tuple(int&& t){
// ...
}
Here is where things get confusing: if make_tuple is passed an lvalue int, what should T be deduced as? The compiler deduces T as int& and uses something called reference collapsing.
Reference collapsing basically states that if the compiler forms a reference to a reference and one of them is lvalue, then the resulting reference is lvalue. Otherwise else, it is an rvalue reference.
The definition of make_tuple (with T deduced) now looks like this:
std::tuple<int&> make_tuple(int& && t){
// ...
}
Which collapses to:
std::tuple<int&> make_tuple(int& t){
// ...
}
So, back to your failed example:
std::tuple<int, int> fraction = make_tuple<int, int>( x, y );
Let's see what make_tuple looks like:
// since you explicitly called make_tuple<int,int>(), then no deduction occurs
std::tuple<int,int> make_tuple(int&& t1, int&& t2){ // Error! rvalue reference cannot bind to lvalue
// ...
}
Now it becomes clear why your example doesn't work. Since no reference collapsing occured, then T&& stayed int&&.
A revised version of your code should look something like this:
auto fraction = std::make_tuple(x,y);
I hope I explained this well.
T&&in a template is not an rvalue reference, it is a forwarding reference, which can bind to lvalues and rvalues.std::make_tuple<int&, int&>( x, y );becausemake_tupleuses forwarding references. But in practice you would just usestd::make_tuple( x, y );and let template argument deduction do the work for you.make_tupleloses its original purpose.tuple<A,B>(a,b), no need formake_tuple. The whole point ofmake_tupleis to let the compiler deduce the types. (later came some changes so we can use plaintuplelikemake_tuple)