2
votes

Since reading some quotes from the cplusplus standard, I'm confused about converting base class type from derived class type. Does this conversion belong to user-defined conversion?

Cite some quote that make me confused about this:

[class.conv]/1 Type conversions of class objects can be specified by constructors and by conversion functions. These conversions are called user-defined conversions and are used for implicit type conversions...

[class.conv.ctor]/3
A non-explicit copy/move constructor ([class.copy]) is a converting constructor.

#include <iostream>
struct Base{
   Base() = default;
   Base(Base const&){}
};
struct Derived:Base{
};
int main(){
   Derived d;
   Base b = d; //from d to b, is this a user-defined conversion? Before reading the standard, I think it's not, but now I'm confused about this.
}

So according to these quotes, a derived class type object to base class type belong to a user-defined conversion. If I miss something written in the standard that says Derived class type to Base class type would not belong to user-defined conversion, please correct me.

2
I don't see anything you've quoted that says that derived-to-base conversions are user-defined. Copy constructors being considered conversion constructors is irrelevant to the nature of derived-to-base conversions. After all, copy constructors don't do the derived-to-base conversions. So why do you think these two unrelated quotes are related? - Nicol Bolas
@NicolBolas In the code ` Base b = d;` Base::Base(Base const&) is invocated,It is a copy constructor,and according to these quotes,copy constructor is a converting constructor,so I think it is - xmh0511
@NicolBolas ??? Copy constructors do perform the derived-to-base conversions. The code above calls the copy constructor Base(Base const&) to initialize b from d. - HTNW
@jackX: The derived-to-base conversion happened during the initialization of the parameter passed to the copy constructor. The act of calling a copy constructor is not by itself a conversion. - Nicol Bolas
@NicolBolas "There is no such standard conversion; this derived-to-base Conversion exists only in the description of implicit conversion sequences" [[over.best.ics]/6](timsong-cpp.github.io/cppwp/n4659/…), [[over.ics.ref]/1](timsong-cpp.github.io/cppwp/n4659/…), I think from argument d to parameter Base const&,It is a derived-to-base conversions as you said,But the inovation of Base::Base(Base const&) mentioned in standard,it self is a copy construcotr - xmh0511

2 Answers

1
votes

OK, let's break down what the standard is saying:

Type conversions of class objects can be specified by constructors and by conversion functions.

Now, let's pretend we know nothing about what these words mean. This sentence talks about a a concept called "type conversions", but it is specifically talking about "type conversions of class objects". So we're not talking about all type conversions, only a subset of them.

Then it says "can be specified", and it lists several ways they can be specified. Next sentence:

These conversions are called user-defined conversions

Note that it doesn't say "these constructors" or "these conversion functions". It says "these conversions." Well, the only "conversions" that have been talked about are the subset previously discussed: "type conversions of class objects". And therefore, this sentence can be restated as:

[type conversions of class objects] are called user-defined conversions.

So, what we can tell from this is that class objects can have type conversions. These conversions can be specified by certain things on the class. And this particular brand of type conversions are called "user-defined conversions".

At no point does the standard say that the constructor itself is either a type conversion or a user-defined conversion. Constructors are just one way to specify such a conversion.

Next, we move on to [class.conv.ctor]/1:

A constructor declared without the function-specifier explicit specifies a conversion from the types of its parameters (if any) to the type of its class. Such a constructor is called a converting constructor.

OK, we now have a definition for "converting constructor". Indeed, given this definition, paragraph 3 (declaring that non-explicit copy/move constructors are converting constructors) is redundant; the above definition makes it clear that they are.

Being a "converting constructor" is a property of a constructor. The process of a user-defined conversion is spelled out, and it can certainly invoke a "converting constructor". But at no time is it stated or implied that this is the only process by which a "converting constructor" can be called.

Therefore, the fact that a copy constructor is a "converting constructor" should not be construed to mean that anything which results in calling a copy constructor is itself a user-defined conversion. User-defined conversions happen when the standard says that they happen.

In the example you describe, what happens is defined in [dcl.init]/17.6.2:

Otherwise, if the initialization is direct-initialization, or if it is copy-initialization where the cv-unqualified version of the source type is the same class as, or a derived class of, the class of the destination, constructors are considered. The applicable constructors are enumerated ([over.match.ctor]), and the best one is chosen through overload resolution. The constructor so selected is called to initialize the object, with the initializer expression or expression-list as its argument(s). If no constructor applies, or the overload resolution is ambiguous, the initialization is ill-formed.

Nowhere in this rule itself is it stated that a conversion of any kind is directly performed. What happens is overload resolution on the single-argument constructors of the destination type. The rules of overload resolution can consider a number of conversions as it tries to fit the given argument to the various parameter possibilities in the overload set. But those are generic, associated with any overload resolution of any function call.

That is, the fact that the function chosen just so happens to be considered a "converting constructor" does not mean that a user-defined conversion has caused it to be called.

0
votes

The user-defined conversions are not considered here. The standard lists two cases for copy initialization (of which the syntax Base b = d; is one form). They are

[dcl.init]/17.16.2

Otherwise, if the initialization is direct-initialization, or if it is copy-initialization where the cv-unqualified version of the source type is the same class as, or a derived class of, the class of the destination, constructors are considered. ...

and

[dcl.init]/17.16.3

Otherwise (i.e., for the remaining copy-initialization cases), user-defined conversions that can convert from the source type to the destination type ... [are used].

This case, since Derived is derived from Base, uses the former clause and not the latter. Thus, only the constructors of Base are consulted, and not e.g. any operator Base() that may be defined in Derived.