2
votes

I saw the following words in the C++ standard draft N4582:

[over.best.ics/4] However, if the target is

(4.1) the first parameter of a constructor or

(4.2) the implicit object parameter of a user-defined conversion function

and the constructor or user-defined conversion function is a candidate by

(4.3) 13.3.1.3, when the argument is the temporary in the second step of a class copy-initialization, or

(4.4) 13.3.1.4, 13.3.1.5, or 13.3.1.6 (in all cases),

user-defined conversion sequences are not considered.

I am confused about the bold part, and don't know how to understand it. I write the following program:

#include <iostream>
using namespace std;    
struct A {
    A(int) {}
    operator int() {cout << "user-defined conversion" << endl; return 0;}
    A(A&) {} //prevent default copy
};
int main()
{
    A a = A(0);
}

It works well in g++ 5.3.0, and output "user-defined conversion", which means a user-defined conversion occurs. Certainly, it can be interpreted as that the temporary A(0) is not a consequence of copy-initialization. Next I change the program to:

#include <iostream>
using namespace std;    
struct A {
    A(int) {}
    operator int() {cout << "user-defined conversion" << endl; return 0;}
    A(A&) {} //prevent default copy
};
A foo() {return A(0);}
int main()
{
    A a = foo();
}

Now the value of foo() is a temporary copy-initialized from A(0), but the program still works. Why would this happen?

2
Your quote is incorrect; you've omitted 4.2 and renumbered 4.3 to 4.2.ecatmur
Do you understand what the "second step" is referring to?T.C.
@ecatmur Sorry for that incorrect quote.xskxzr
@T.C. No, I don't really understand the meaning of "second step".xskxzr

2 Answers

4
votes

You can go read [dcl.init]/17 for the actual standardese. The "second step" here is referring to copy-initializing a variable of class type A from something b of an unrelated type. In such a case, copy-initialization happens in two steps:

  • Step 1: you implicitly convert b to A. If you call a converting constructor for this, it creates a temporary A.
  • Step 2: you then initialize the A variable from the result of the conversion. (In sane classes, this is typically elided.)

What that quote is saying is that you don't do user-defined conversions in this second step.

For example, with your A, A a = 0;. In the first step, you make an A temporary from 0. In the second step, you try to initialize a with that temporary - without using user-defined conversions. That fails, because neither A constructor is viable.

0
votes

There is only 2 ctors, one that take a reference to an existing object (no temporary) and one that takes an int.

Your code produces (with additional messages):

int ctor
user-defined conversion
int ctor
user-defined conversion
int ctor

The first is the construction inside foo.

The second is due to the fact that the returned value can't be constructed by copy as you prevented it, so the compiler convert the value constructed to an int.

The third is because it uses the ctor that takes an int to build the returned value.

The fourth is (as the second) due to the fact that the returned value can't be used to construct a, so it convert it to an int.

The fifth is because it then uses the ctor that takes an intto build a.