Every C++ class can declare conversion operators in other types and in particular the operator can be template and can convert to const reference type. But it looks like this operator is treated differently by the modern compilers. The next example:
struct B {};
struct A {
template <typename T> operator const T &();
operator B() = delete;
};
int main() {
A a;
[[maybe_unused]] B b(a); //error in Clang
[[maybe_unused]] int c(a); //error in GCC
}
is fully accepted by MSVC.
GCC accepts a conversion from A
to B
despite the fact that non-template A::operator B
, which I would expect to be preferred, is explicitly deleted.
At the same time GCC rejects the conversion from A
to int
:
cannot convert 'A' to 'int' in initialization
Demo: https://gcc.godbolt.org/z/WvafjPPjr
I personally prefer the behavior of Clang here, but is it actually right?
You can define a member function of a class, called a conversion function, that converts from the type of its class to another specified type.
A conversion operator, in C#, is an operator that is used to declare a conversion on a user-defined type so that an object of that type can be converted to or from another user-defined type or basic type. The two different types of user-defined conversions include implicit and explicit conversions.
Conversion functions define conversions from a user-defined type to other types. These functions are sometimes referred to as "cast operators" because they, along with conversion constructors, are called when a value is cast to a different type.
3) Declares a user-defined conversion function that is conditionally explicit. conversion-type-id is a type-id except that function and array operators [] or () are not allowed in its declarator (thus conversion to types such as pointer to array requires a type alias/typedef or an identity template: see below).
In this article we will see what is the conversion operator in C++. C++ supports object oriented design. So we can create classes of some real world objects as concrete types. Sometimes we need to convert some concrete type objects to some other type objects or some primitive datatypes.
So we can create classes of some real world objects as concrete types. Sometimes we need to convert some concrete type objects to some other type objects or some primitive datatypes. To make this conversion we can use conversion operator.
If both conversion functions and converting constructors can be used to perform some user-defined conversion, the conversion functions and constructors are both considered by overload resolution in copy-initialization and reference-initialization contexts, but only the constructors are considered in direct-initialization contexts.
B b(a);
is direct-initialization for class type. Per [dcl.init.general], constructors of B
are considered in overload resolution.
B
has three implicitly-generated constructors:
B(); // #1
B(const B&); // #2
B(B&&); // #3
Per [over.match.viable], only #2 and #3 are viable, as #1 does not have enough parameters.
Overload resolution tries to form an implicit conversion sequence from a
to the parameter type for each constructor.
Per [conv.general], conversion to reference is equivalent to reference initialization.
Per [dcl.init.ref], in the initialization of const B&
, if the source expression can be converted to an lvalue of type const B
via operator const B&
, then the reference binds to the result of conversion.
Again per [dcl.init.ref], in the initialization of B&&
, if the source expression can be converted to an rvalue of type B
via operator B
, then the reference binds to the result of conversion. Otherwise, again per [dcl.init.ref], other user-defined conversions are considered.
Since a
cannot be converted to B&&
in any way, and it can be converted to const B&
, #2 wins. So a
is converted to const B&
and then is used to initialize b
via the copy constructor.
int c(a);
is direct-initialization for non-class type. Per [dcl.init.general], conversion functions of A
are considered in overload resolution.
Per [temp.deduct.conv], the template argument of operator const T &
can be deduced as int
.
Since the result of operator const int&
can be converted to int
, the initialization is well-formed.
So I believe that both initializations are valid. That is, MSVC is right. However, the "can be converted" phrasing in [dcl.init.ref] is sloppy and might intend to mean that a value can theoretically be converted via a deleted conversion function, in which case Clang might be right.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With