I am trying understand the reference initialization in C++, especially for initializing lvalue reference to "const" and rvalue reference.
As I read the standard draft in here: https://eel.is/c++draft/dcl.init.ref#5.4.1
If T1 or T2 is a class type, user-defined conversions are considered using the rules for copy-initialization of an object of type “cv1 T1” by user-defined conversion ([dcl.init], [over.match.copy], [over.match.conv])
I found that when T2 requires a conversion to T1, apart from the conversion constructors of T1, it also allows conversion functions in T2.
Originally, I thought that this is redundant because the cases of conversion functions have already been covered in: https://eel.is/c++draft/dcl.init.ref#5.3.2
has a class type (i.e., T2 is a class type), where T1 is not reference-related to T2, and can be converted to an rvalue of type “cv3 T3” or an lvalue of function type “cv3 T3”, where “cv1 T1” is reference-compatible with “cv3 T3” (see [over.match.ref])
So, my first question is it redundant to include conversion functions in 5.4.1?
Later I found that there is a slightly difference in 5.3.2 and 5.4.1.
For example, in 5.3.2, T2 can be converted to a derived class of T1. But in 5.4.1, the temporary object is created as T1.
So I wrote the following simple code to test it:
#include<iostream>
class X
{
public:
virtual int get()
{
return 1;
}
};
class Y : public X
{
public:
int get()
{
return 2;
}
};
class Z
{
public:
operator const Y () const
{
return Y();
}
};
int main()
{
Z z;
X&& r = z;
std::cout << r.get() << std::endl;
return 0;
}
Output in one of the compiler:
main.cpp: In function 'int main()':
main.cpp:46:12: error: binding reference of type 'X&&' to 'const X' discards qualifiers
46 | X&& r = z;
| ^
main.cpp:40:7: note: after user-defined conversion: 'Z::operator const Y() const'
40 | operator const Y () const {return Y();}
I expected that z is converted to a temporary object of type X as stated in 5.4.1. But it results in a compiler error saying that can't convert z to X. If I changed "operator const Y" to "operator Y", it can compile and print 2. But this actually falls into the case of 5.3.2, not 5.4.1, which is not what I wanted to test. Although the compiler says that it can't convert z to type X, if I changed "X&& r= z;" to "X r = z;", the compiler can compile and print 1 which means what it claims (can't convert z to type X) is wrong.
I tested it in both cpp.sh and coliru.stacked-crooked.com.
So it seems that the compiler fails to implement the case in 5.4.1 of using conversion functions, or did I misunderstand the standard draft?
Edit:
To make my question clear, I am intentionally using "operator const Y" instead of "operator Y" so that "const Y" is not reference-compatible to "cv1 T1" and it will not fall into the case 5.3.2. I expected 5.4.1 will be used and a temporary of type X (not const X) is copy-initialized from z. If 5.4.1. also requires "const X", it is redundant as 5.3.2. It will be meaningless to include the conversion functions in 5.4.1. (5.4.1. should only include conversion constructor). So I thought either the compiler is wrong (it fails to handle 5.4.1) or the standard is redundant (if the standard intends to require "const X" instead of "X"). Or anyone can provide a situation where 5.4.1 will use conversion functions?
X&& r = z;toconst X&& r = z;.