Reputation: 15478
Probably my understanding of explicit
is insufficient, but I wonder why in the following code the copy constructor is not hidden by the unversal reference constructor when I declare the latter as explicit
.
struct A
{
A() = default;
template<typename T>
A(T&& t) { std::cout<<"hides copy constructor"<<std::endl; }
};
struct A_explicit
{
A_explicit() = default;
template<typename T>
explicit A_explicit(T&& t) { std::cout<<"does not hide copy constructor?"<<std::endl; }
};
int main()
{
A a;
auto b = a; (void) b; //prints "hides copy constructor"
A_explicit a_exp;
auto b_exp = a_exp; (void) b_exp; //prints nothing
}
Is that a general solution instead of the SFINAE stuff one would apply otherwise to prevent the hiding in A
(for example by std::enable_if_t<!std::is_same<std::decay_t<T>, A>::value>
, see here)?
Upvotes: 2
Views: 388
Reputation: 137330
A constructor marked as explicit
does not participate in overload resolution during copy-initialization (A a = b;
, among other things).
It does participate in copy-list-initialization (A a = {b1};
), and causes the program to be ill-formed if selected.
... except when the thing inside the braces is an A
or a class derived therefrom, in which case a recent defect report changed the rules to say that in this particular situation copy-initialization is performed instead - and so explicit
constructors are once again just ignored entirely (demo).
Very teachable, I know.
Is that a general solution instead of the SFINAE stuff one would apply otherwise to prevent the hiding in A?
No. Because that constructor will still win overload resolution for direct-initialization:
A_explicit a, b(a); // will call the constructor taking a forwarding reference
Upvotes: 5
Reputation: 119164
In A
, the copy constructor is not hidden. The compiler implicitly declares it, as it always does. It simply loses overload resolution because its parameter type (const A&
) has extra cv-qualification compared to the parameter of the specialization of the constructor template (A&
). If you would do
auto b = static_cast<const A&>(a);
you would see that the copy constructor would be called.
In A_explicit
, the template is not submitted as a candidate to overload resolution at all, because it is declared explicit
. The implicitly declared copy constructor is still there, like it is in A
, so it is called.
Upvotes: 6