Description
In [dcl.init.ref] p4, the "reference-related" is defined as
Given types “cv1 T1” and “cv2 T2”, “cv1 T1” is reference-related to “cv2 T2” if T1 is similar ([conv.qual]) to T2, or T1 is a base class of T2.
Consider this example
int a;
int&& rf = a; // ill-formed
Presumably, it is restricted by [dcl.init.ref#5.4.4]
If T1 is reference-related to T2:
- [...]
- if the reference is an rvalue reference, the initializer expression shall not be an lvalue.
Is the type int
similar to int
? It should be determined by [conv.qual] p2
Two types T1 and T2 are similar if they have qualification-decompositions with the same n such that corresponding Pi components are either the same or one is “array of Ni" and the other is “array of unknown bound of”, and the types denoted by U are the same.
However, the qualification-decomposition of type int
does not have any Pi component. Maybe, one could argue since they are empty, they're the same.
From a prvalue of type int
to type int
, or from a prvalue of type int
to int const volatile
, Is it a qualified conversion? It seems [conv.qual] p3 can apply to these two conversions except that whether [conv.qual#3.2] is suitable for empty Pi, however, we can also say it's [conv.integral]
A prvalue of an integer type can be converted to a prvalue of another integer type.
Types bool, char, wchar_t, char8_t, char16_t, char32_t, and the signed and unsigned integer types, and cv-qualified versions ([basic.type.qualifier]) thereof, are collectively termed integral types. A synonym for integral type is integer type.
Maybe, we should give this conversion accurate meaning.
For class type, it is a bit different, since a prvalue of cv class type can retain the cv-qualification. So, assume T
is a class type, from a prvalue of type volatile const T
to type T
is a user-defined conversion? Why I say it is a user-defined conversion because of [over.best.ics#general-6] and [over.ics.user]
When the parameter has a class type and the argument expression has the same type, the implicit conversion sequence is an identity conversion. When the parameter has a class type and the argument expression has a derived class type, the implicit conversion sequence is a derived-to-base conversion from the derived class to the base class.
A conversion of an expression of class type to the same class type is given Exact Match rank, and a conversion of an expression of class type to a base class of that type is given Conversion rank, in spite of the fact that a constructor (i.e., a user-defined conversion function) is called for those cases.
Since T
and volatile const T
are not the same type, hence the conversion is user-defined conversion?
struct Base{
Base() = default;
Base(Base const volatile&){}
};
struct A:Base{
A() = default;
A(A const volatile&){}
};
struct B{B(A const volatile&){}};
void fun(A){std::cout<<"#1\n";}
void fun(Base){std::cout<<"#2\n";}
void fun(B){std::cout<<"#3\n";}
void fun2(Base){std::cout<<"#4\n";}
void fun2(B){std::cout<<"#5\n";}
int main() {
A volatile const a;
fun(a);
fun2(a);
}
Obviously, #1
and #4
are called. Maybe we should fix [over.best.ics#general-6] and [over.ics.user] to that
When the parameter has a class type and the cv-unqualified version of argument expression's type has the same type, the implicit conversion sequence is an identity conversion. When the parameter has a class type and the argument expression has a (possible cv-qualified) derived class type, the implicit conversion sequence is a derived-to-base conversion from the derived class to the base class.
A conversion of an expression of "cv1 T1" to "cv2 T2" is given Exact Match rank if "T1" and “T2” are the same class type, and a conversion of an expression of "cv1 T1" to "cv2 T2" is given Conversion rank if T2 is a base class of T1 and "cv2 T2" is reference-compatible with "cv1 T1" , in spite of the fact that a constructor (i.e., a user-defined conversion function) is called for those cases.
These issues arise from the permitted types.