It is known that we can't initialize rvalue reference with lvalue, but why can function name?

It is said that we can't initialize an rvalue reference with an lvalue. For example:
1
2
3
int i=0;
int && irr = std::move(i);
int && irr2 = irr;
In the above code, `irr` is the name of a variable, thus an lvalue even if it refers to an rvalue. So, the compiler would report an error.

Below is excepted from the document of value categories: https://en.cppreference.com/w/cpp/language/value_category
The following expressions are lvalue expressions:
the name of a variable, a function, a template parameter object(since C++20), or a data member, regardless of type, such as std::cin or std::endl. Even if the variable's type is rvalue reference, the expression consisting of its name is an lvalue expression (but see Move-eligible expressions);

But if I tried to initialize an rvalue reference with a name of function, there is no error:
1
2
int Test(int i) { return ++i; }
decltype(Test) && f = Test;
According to the cited doc, `Test` is the name of a function, so it is an lvalue. `decltype(Test)` is a pure function type `int(int)` which is checked by `typeid(decltype(Test)).name()`. So, there is no reference collapsing. Also, there is no function-to-pointer conversion here so `Test` is not converted to a pointer to function which is rvalue. Therefore `f` is an rvalue reference which should not accept an lvalue initializer `Test`. But the compiler just doesn't report an error due to it. Why?

PS, I also tried `typedef`
1
2
3
int Test(int i) { return ++i; }
typedef int Func(int);
Func&& f = Test;
but the result is the same.
Last edited on
It's a special case:
https://cplusplus.com/forum/general/273175/#msg1178129

My opinion is that the distinction between lvalue and rvalue is not relevant for functions, and that this special case exists to ease generic programming.
Last edited on
@mbozzi: I disagree. Your link does nothing but repeat my code and say "look, it works". I don't think this is a good manner to answer. You seemed to have confused my question with reference collapsing (maybe I am wrong, but I can see no connection between your reply and my question). I said it clearly in my question that this is NOT a reference collapsing issue. I don't know whether you had arrived at that place before you couldn't wait to reply. Maybe you just read a couple of words in my question and excited to think you caught a duplicate without really understanding what I was asking. Fortunately you can't mark my question as duplicate like in Stackoverflow. Read, make sure you understand, think, and then answer. If you don't have the patience to do that, just leave the question to other people. I don't wanna see the bad atmosphere of Stackoverflow spread here. Thanks.
I can see no connection between your reply and my question

The connection is that you're allowed to do what you're asking about because of special-case wording in the standard. It's an exception to the rules.

I'm not able to locate the wording in the time I have available, but I'm fairly sure that it exists. It would have been better if I could find it, but at least you've got a research lead now.

I'll repeat the sample code I linked to:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
#include <type_traits>

void f() {};
int x;

int main()
{
    static_assert(std::is_same_v<decltype((f)), void(&)()>,
      "(f) is an lvalue expression with function type");
    
    // both rvalue and lvalue references to void() can be initialized from a 
    // lvalue expression of type void()
    void(&&rrf)() = f;
    void(&rf)() = f;
    
    // The same is not true for non-function types
    int&& rrx = x; // error: binding rvalue reference to lvalue
    int&  rx  = x;
}
There's no reference collapsing in this program.
Last edited on
Let me confirm your answer first. The link you gave me is "No, because rvalue references can be initialized, as a special case, from lvalue expressions with function type. I cited the standard about this in your prior thread." Right? And the standard you cited is two floors above: "http://eel.is/c++draft/temp.deduct.call#3.sentence-3". Is that right?

If that is the answer you provided, to repeat what I said, your answer does nothing but repeat my code and say "look, it works", and I can see no connection between your reply and my question. I have seen your code therein so there is no need to copy it here again. I have never heard of such a special case of initializing rvalue reference with lvalue expression having function type. There are indeed special rules in C++ standard like converting initialize list to std::initializer_list in auto deduction, but I don't think there is such an inconsistent pain in the standard related to your special case. As a side note, the question in your link is related to reference collapsing, and you answered with your special case for function type, which, unless you have found the item in the standard text, is wrong and have misled the asker in my understanding.
And the standard you cited is two floors above: "http://eel.is/c++draft/temp.deduct.call#3.sentence-3". Is that right?

No, the citation of interest was in an entirely different thread (a different web page), which I have lost.

I have never heard of such a special case of initializing rvalue reference with lvalue expression having function type.

Alright, but the sample program I provided serves as evidence that it is allowed.

In addition, I found the appropriate passage in the std
See [dcl.init.ref]/5, in particular 5.3.1
https://eel.is/c++draft/dcl.init.ref#5.3.1

See also cppreference's page on reference initialization
https://en.cppreference.com/w/cpp/language/reference_initialization
Last edited on
Registered users can post here. Sign in or register to post.