3

So I wrote a code in C++ 11

#include <iostream>
using namespace std;

void print (int &&a)
{
    cout<<"rval ref";
}

void print (const int& a)
{
    cout<<"const ref";
}

int main()
{
    print(9);   
}

The output of code was fascinating that it is "rval refernce" But if I rewrite the code just removing a function defination:

#include <iostream>
using namespace std;

void print (const int& a)
{
    cout<<"const ref";
}

int main()
{
    print(9);   
}

I get output as "const ref"

Edit: There's one more thing here if I rewrite code again as

#include <iostream>
using namespace std;

void print (int &&a)
{
    cout<<"rval ref";
}

void print (const int&& a)
{
    cout<<"const ref";
}

int main()
{
    print(9);   
}

Still its printing "rval ref" , please explain the logic

Can someone explain why C++ give preference to && over const while passing a ravlue as argument ?## Heading ##

user438383
  • 5,716
  • 8
  • 28
  • 43
simplekind
  • 39
  • 4
  • 7
    A literal like `9` is an rvalue, so the rvalue reference overload is preferred. – François Andrieux Jun 29 '21 at 19:27
  • 4
    There's not much more to it than what you see. `9` is an rvalue, so the overload with the rvalue ref parameter has precedence. But const lvalue refs can also bind to rvalues. – HolyBlackCat Jun 29 '21 at 19:28
  • 1
    Why wouldn't an *rvalue* reference get priority for rvalues over an *lvalue* reference? – NathanOliver Jun 29 '21 at 19:28
  • The pararmeter "9" is not const. So `print (int &&a)` is used, better match than `print (const int& a)` – Ripi2 Jun 29 '21 at 19:29
  • why do you think a literal int =9 can be a ref? – ΦXocę 웃 Пepeúpa ツ Jun 29 '21 at 19:30
  • All the details - https://en.cppreference.com/w/cpp/language/overload_resolution – Richard Critten Jun 29 '21 at 19:30
  • @Ripi2 — the integer constant `9` is definitely `const`. Try feeding it to a function that takes `int&`. – Pete Becker Jun 29 '21 at 19:40
  • 1
    An integer literal is a prvalue - https://en.cppreference.com/w/cpp/language/value_category which is a rvalue and therefore prefers to bind to an rvalue reference – Richard Critten Jun 29 '21 at 19:43
  • @RichardCritten I am kind of beginner so its hard for me to understand the links you sent , can you briefly explain what does they say? – simplekind Jun 29 '21 at 19:49
  • C++ has types and value categories - I don't think I can explain this in a few words sorry. It's chapter of a book level of explaining. The intro para from the above link is better than I could ever do. – Richard Critten Jun 29 '21 at 19:50
  • 2
    The exact quote from the above link is _"__rvalue__: ...When used as a function argument and when two overloads of the function are available, one taking rvalue reference parameter and the other taking lvalue reference to const parameter, an rvalue binds to the rvalue reference overload ..."_ – Richard Critten Jun 29 '21 at 20:00
  • @RichardCritten thanks for the information, I will surf more on function overloading on internet – simplekind Jun 30 '21 at 08:34

1 Answers1

1

Lets see on case by case basis what is happening:

Case 1

Here we consider:

void print (int &&a)
{
    cout<<"rval ref";
}

void print (const int& a)
{
    cout<<"const ref";
}

int main()
{
    print(9); //chooses print(int&&) version  
}

The behavior of this case 1 can be understood from over.ics.rank which states:

3.2 Standard conversion sequence S1 is a better conversion sequence than standard conversion sequence S2 if

S1 and S2 are reference bindings ([dcl.init.ref]) and neither refers to an implicit object parameter of a non-static member function declared without a ref-qualifier, and S1 binds an rvalue reference to an rvalue and S2 binds an lvalue reference.

(emphasis mine)

This means that in your this case 1, the version with int&& is preferred over const int&.


Case 2

Here we consider:

void print (const int& a)
{
    cout<<"const ref";
}

int main()
{
    print(9);   //chooses the only viable and available option print(const int&)
}

Here in case 2, since an lvalue reference to const object is allowed to bind to an rvalue, the provided print(const int&) is viable and is the only one available and hence is selected.

Case 3

Here we consider:

void print (int &&a)
{
    cout<<"rval ref";
}

void print (const int&& a)
{
    cout<<"const ref";
}

int main()
{
    print(9);   
}

Now, this chooses the first version print(int&&) because 9 is an int prvalue and not a const int prvalue. Note also that for a const int prvalue, its const will be stripped off before any further analysis and so the first version print(int&&) will be selected. This is as specified in expr#6:

If a prvalue initially has the type cv T, where T is a cv-unqualified non-class, non-array type, the type of the expression is adjusted to T prior to any further analysis.

This means that for class type const prvalues the second version of the function print with print(const C&&) will be selected unlike the built in type int as demonstrated below.

struct C 
{
    C()
    {
        std::cout<<"default ctor"<<std::endl;
    }
};
void print (C &&a)
{
    cout<<"rval ref";
}

void print (const C&& a)
{
    cout<<"const ref";
}
const C func()
{
    const C temp;
    return temp;
}
int main()
{
    print(func()); //prints const ref  
}

The output of the above program for class type C is:

default ctor
const ref

As is evident from the above example, for class types the second version of print with const C&& will be selected.

Jason
  • 36,170
  • 5
  • 26
  • 60
  • I think the OP is puzzled why a literal `9` does not bind to a const rvalue reference -- I mean, there is nothing more const than a literal , is there? – Peter - Reinstate Monica May 24 '22 at 11:20
  • @Peter-ReinstateMonica This is because for built in types like `int` the `const int` prvalue is adjusted to `int` meaning its `const` is stripped off. So even if there was a `const int` prvalue its `const` will be stripped off and hence the first `print` version with `int&&` will be selected. But this stripping off of const doesn't happen for class types and so if we were to pass a class type const prvalue the second version of `print` will be selected. I have added more detailed explanation for this in my answer. Check out my updated answer(case 3 in particular). – Jason May 24 '22 at 11:46