Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Ternary allowed to call an explicit copy constructor implicitly?

Consider the code below:

#include <cstdio>
struct A
{
    A(){}
    explicit A(const A&) {std::puts("copy");}
};

int main() 
{
    A a;
    true ? a : A();
    return 0;
}

As I understand the ternary would try to copy a and should fail since the copy constructor is explicit, however gcc compiles this just fine and creates a copy. Clang spits out an error as expected.

Is this a bug in gcc?

I'm using gcc 8.1 and clang 7.0, in c++17 mode, but I also tried all versions of gcc in compiler explorer in c++98 mode, and they all behave the same.

like image 658
namark Avatar asked Oct 09 '18 11:10

namark


1 Answers

Clang is right to reject it, and it is indeed a GCC bug. I'll quote n4659 (closest document I have to to the C++17 standard) for simplicity.

First and foremost, the type of of the conditional expression in your example, as specified by [expr.cond] ¶6 must be a prvalue of type A.

Now, according to [expr.cond] ¶7, emphasis mine:

Lvalue-to-rvalue, array-to-pointer, and function-to-pointer standard conversions are performed on the second and third operands.

a must be able to undergo the lvalue-to-rvalue conversion. Which for a is specified in [conv.lval] ¶3.2 (again, emphasis mine) as

Otherwise, if T has a class type, the conversion copy-initializes the result object from the glvalue.

Copy initialization of an A from an A, in any context, should pick a converting constructor in overload resolution ([over.match.copy] ¶1.1):

The converting constructors of T are candidate functions.

And an explicit copy constructor is not a converting constructor ([class.conv.ctor] ¶3)

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

A conforming C++ implementation cannot accept the conditional expression your wrote as well-formed.

like image 175
StoryTeller - Unslander Monica Avatar answered Nov 10 '22 12:11

StoryTeller - Unslander Monica