2
#include <iostream>

void g(int&) {
    std::cout << "int&" << std::endl;
}

void g(int&&) {
    std::cout << "int&&" << std::endl;
}

template<typename T>
void func(T&& x) {
    std::cout << "func(T&&)\n=================\n";
    std::cout << "int: " << std::is_same_v<decltype(x), int> << std::endl;
    std::cout << "int&: " << std::is_same_v<decltype(x), int&> << std::endl;
    std::cout << "int&&: " << std::is_same_v<decltype(x), int&&> << std::endl;
    std::cout << "=================\n";
    g(x);
    auto tmp = std::move(x);
    g(tmp);
    g(std::move(x));
}

int main() {
    func(2);
}

Output:

func(T&&)
=================
int: 0
int&: 0
int&&: 1
=================
int&
int&
int&&

Why does this code int&& overloading if I put std::move() inside of calling g() function?

I know, that I can solve this problem using std::forward, but I can't get why it doesn't work.

==================================================

UPDATE: Because of several answers, I have one more question:

In this code:

#include <iostream>
#include <string>

using namespace std;

template<typename T>
void foo(T&& bar){
    bar = T();
    std::cout << bar;
}

int main() {
    foo(22);    // OK
    foo("qwe");   // NOT OK
}

If I leave foo(22), it works great, but it won't work if I leave foo("qwe"), why?

I tested a lot of cases.

Remy Lebeau
  • 555,201
  • 31
  • 458
  • 770
OHtuzh
  • 21
  • 3
  • 3
    related/dupe: https://stackoverflow.com/questions/28483250/rvalue-reference-is-treated-as-an-lvalue – NathanOliver Feb 17 '23 at 22:46
  • 5
    `x` has a name; its an lvalue. Thats why we need `std::forward`. – tkausl Feb 17 '23 at 22:47
  • The `auto` in `auto tmp = std::move(x);` is `int`, not `int&` or `int&&` or something else. `g(tmp)` picks the lvalue overload for the exact same reason that `g(x)` does, even aside from the issues raised in Nathan Oliver's duplicate. – Nathan Pierson Feb 17 '23 at 22:50
  • @NathanOliver No, it doesn't. This question is about universal references, but my question is about why universal reference choose that exact function overloading if universal reference is actually rvalue reference – OHtuzh Feb 17 '23 at 22:50
  • 1
    `auto tmp = std::move(x);` the `tmp` is not an rvalue, it's an lvalue. – Eljay Feb 17 '23 at 22:50
  • @NathanPierson I've just tested. Replace auto->int&& and output hasn't changed – OHtuzh Feb 17 '23 at 22:52
  • That's why I said "even aside from the issues raised in [the] duplicate". – Nathan Pierson Feb 17 '23 at 22:52
  • 1
    Do a search for `reference collapse`. – Martin York Feb 17 '23 at 22:52
  • @Eljay Firstly, as I said, replace auto to int&& didn't affect Secondly, why g(x) choose g(int&) overloading, but not the g(int&&) overloading – OHtuzh Feb 17 '23 at 22:54
  • @MartinYork I know how reference collapse works. Check the code and output. X is `int&&`, because `std::is_same_v` returns `true`. – OHtuzh Feb 17 '23 at 22:56
  • "_This question is about universal references,_": I don't see how. You are asking why the overloads of `g` are chosen, not how calling `func` behaves. `g` doesn't use any universal references. Nothing changes if you replace `T&&` with `int&&` and remove the template head. – user17732522 Feb 17 '23 at 22:58
  • 2
    Like many others, you are confusing concept of type and value category. These two are basically unrelated to each other. No matter the type, if you refer to variable by name, it's lvalue. You can have lvalue of type rvalue reference, there's absolutely no problem with that. `int&& var = 2;` is of type rvalue reference to int, but `var` is lvalue by itself. – Yksisarvinen Feb 17 '23 at 22:58
  • Like an array collapse to a pointer. An r-value will collapse to an l-value at the drop of a hat. When you use x as a variable its type collapses to an l-value reference. – Martin York Feb 17 '23 at 23:02
  • @Yksisarvinen Did I understand you correctly? In this case, when I call `func(2)`. I am creating `x` that is actually is `lvalue` but "contains" `rvalue reference`. And when I am passing `x` into `g()` it choose `g(int&)` because `x` is `lvalue` – OHtuzh Feb 17 '23 at 23:04
  • @Yksisarvinen I added one subquestion into the post. Please check it – OHtuzh Feb 17 '23 at 23:13
  • The `g(int&&)` overloading requires an rvalue (or xvalue). – Eljay Feb 17 '23 at 23:17
  • *But It won't work if I leave foo("qwe")* `"qwe"` is c-string and it has the type `const char[4]`. Arrays are not assignable so that's why `bar = T();` fails to compile. – NathanOliver Feb 17 '23 at 23:29
  • Because a `&&` parameter in a **template** function is not an `&&` parameter in a non-template function. – Eljay Feb 17 '23 at 23:43

0 Answers0