Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

What are the properties of template conversion `operator const T &` in C++?

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?

like image 613
Fedor Avatar asked Sep 09 '21 20:09

Fedor


People also ask

What is conversion function in c++?

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.

What is a conversion operator?

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.

What is an user-defined type conversion?

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.

What is a conversion-type-ID declarator?

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).

What is the conversion operator in C++?

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.

How to convert concrete type objects to other type objects?

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.

What is the difference between conversion functions and converting Constructors?

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.


Video Answer


1 Answers

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.

like image 194
cpplearner Avatar answered Nov 04 '22 15:11

cpplearner