The second snippet below, why doesn't it compile?

53 views
Skip to first unread message

Belloc

unread,
Sep 2, 2016, 3:19:04 PM9/2/16
to ISO C++ Standard - Discussion
The snippet below compiles

#include <iostream>
struct A {
    A
(const A&) { std::cout << "A::A(const A&)" << '\n'; }
    A
() { std::cout << "A::A()" << '\n'; }
    A
(int) { std::cout << "A::A(int)" << '\n'; }
};


struct B {
   
explicit operator A() const { std::cout << "explicit B::operator A() const" << '\n'; return A{}; }
    B
() { std::cout << "B::B()" << '\n'; }
};


int main(){
    B b
;
    A a
(b);
}

and prints the following (see live example)

B::B()
explicit B::operator A() const
A
::A()
A
::A(const A&)
A
::A(const A&)

which seems to be Ok with me.

But why doesn't this compile? (see live example)

#include <iostream>
struct A {
    A
(const A&) { std::cout << "A::A(const A&)" << '\n'; }
    A
() { std::cout << "A::A()" << '\n'; }
    A
(int) { std::cout << "A::A(int)" << '\n'; }
};


struct B {
   
explicit operator int() const { std::cout << "explicit B::operator int() cont" << '\n'; return 1; }
    B
() { std::cout << "B::B()" << '\n'; }
};


int main(){
    B b
;
    A a
(b);
}

As far as I can understand, it should compile, as B::operator int() is being used to direct-initialize a in main(). See [class.conv.fct]/2.

Richard Smith

unread,
Sep 2, 2016, 5:18:17 PM9/2/16
to std-dis...@isocpp.org
As a general (approximate) rule of thumb, you only get to use an explicit conversion function if you explicitly name the type you're converting to. So you can use 'explicit operator int' to convert to 'int' but not to 'A'.

In this specific case, we consider constructors of 'A' (including explicit ones, because we're using direct-initialization). For the desired 'A(int)' constructor, we try to form an implicit conversion sequence from 'b' to its parameter type 'int'. Implicit conversion sequences model copy-initialization (just like copy-initialization will be used to initialize the parameter from the argument if the constructor is selected). And copy-initialization doesn't get to use explicit conversion functions (see [over.match.conv]/1.1).

Belloc

unread,
Sep 2, 2016, 6:18:03 PM9/2/16
to ISO C++ Standard - Discussion


On Friday, September 2, 2016 at 6:18:17 PM UTC-3, Richard Smith wrote:

As a general (approximate) rule of thumb, you only get to use an explicit conversion function if you explicitly name the type you're converting to. So you can use 'explicit operator int' to convert to 'int' but not to 'A'.

In this specific case, we consider constructors of 'A' (including explicit ones, because we're using direct-initialization). For the desired 'A(int)' constructor, we try to form an implicit conversion sequence from 'b' to its parameter type 'int'. Implicit conversion sequences model copy-initialization (just like copy-initialization will be used to initialize the parameter from the argument if the constructor is selected). And copy-initialization doesn't get to use explicit conversion functions (see [over.match.conv]/1.1).

Thanks for your reply. Let me just check if I'm following you correctly. You're basically saying that, notwithstanding the fact that the initialization A a(b); is a direct-initialization, the compiler considers the initialization of the parameter in the constructor A(int) to be a copy-initialization, and because of this, an explicit conversion function is not accepted. Is that correct?  

Also in [over.match.conv]/2 we have: " [ Note: This argument will be compared against the implicit object parameter of the conversion functions.  — end note ]" the implicit doesn't seem to be correct. Could you clarify?

Richard Smith

unread,
Sep 2, 2016, 9:11:17 PM9/2/16
to std-dis...@isocpp.org
On Fri, Sep 2, 2016 at 3:18 PM, Belloc <jabe...@gmail.com> wrote:
On Friday, September 2, 2016 at 6:18:17 PM UTC-3, Richard Smith wrote:

As a general (approximate) rule of thumb, you only get to use an explicit conversion function if you explicitly name the type you're converting to. So you can use 'explicit operator int' to convert to 'int' but not to 'A'.

In this specific case, we consider constructors of 'A' (including explicit ones, because we're using direct-initialization). For the desired 'A(int)' constructor, we try to form an implicit conversion sequence from 'b' to its parameter type 'int'. Implicit conversion sequences model copy-initialization (just like copy-initialization will be used to initialize the parameter from the argument if the constructor is selected). And copy-initialization doesn't get to use explicit conversion functions (see [over.match.conv]/1.1).

Thanks for your reply. Let me just check if I'm following you correctly. You're basically saying that, notwithstanding the fact that the initialization A a(b); is a direct-initialization, the compiler considers the initialization of the parameter in the constructor A(int) to be a copy-initialization, and because of this, an explicit conversion function is not accepted. Is that correct?  

Yes. Copy-initialization is always used when initializing function parameters, no matter what syntax is used to make the function call.
 
Also in [over.match.conv]/2 we have: " [ Note: This argument will be compared against the implicit object parameter of the conversion functions.  — end note ]" the implicit doesn't seem to be correct. Could you clarify?

Sure. The "implicit object parameter" is a term defined in [over.match.funcs]/2 that names an imaginary parameter that the compiler invents when performing overload resolution involving member functions. The word "implicit" here is just part of the name of the defined term, and doesn't mean anything else.

Belloc

unread,
Sep 3, 2016, 11:01:13 AM9/3/16
to ISO C++ Standard - Discussion
Before I posted this question I tried for several hours to understand by myself what was occurring, just by reading the Standard, to no avail. I must say that your answer allowed me to have now, a pretty good understanding of both paragraphs [over.match.copy] and [over.match.conv]. Thanks a lot. 
Reply all
Reply to author
Forward
0 new messages