template <typename T> struct A {
A(T);
A(const A&);
};
int main()
{
A x(42); // #1
A y = x; // #2
}
As I understand,T
for #1 will be deduced using the implicit deduction guide
generated from the first ctor. Then x
will be initialized using that ctor.
For #2 though, T
will be deduced using the copy deduction candidate (which, as I understand, is a specific case of a deduction guide) (and then y
will be initialized using the 2nd ctor).
Why couldn't T
for #2 be deduced using the (implicit) deduction guide generated from the copy-ctor?
I guess I just don't understand the general purpose of the copy deduction candidate.
The initial draft to add the wording for copy deduction was P0620R0, which mentions
This paper is intended to resolve
- The direction on wrapping vs. copying from EWG on Monday in Kona
Some notes on that meeting are available on https://botondballo.wordpress.com/2017/03/27/trip-report-c-standards-meeting-in-kona-february-2017/:
Copying vs. wrapping behaviour. Suppose
a
is a variable of typetuple<int, int>
, and we writetuple b{a};
. Should the type ofb
betuple<int, int>
(the "copying" behaviour), ortuple<tuple<int, int>>
(the "wrapping" behaviour)? This question arises for any wrapper-like type (such aspair
,tuple
, oroptional
) which has both a copy constructor and a constructor that takes an object of the type being wrapped. EWG felt copying was the best default. There was some talk of making the behaviour dependent on the syntax of the initialization (e.g. the{ }
syntax should always wrap) but EWG felt introducing new inconsistencies between the behaviours of different initialization syntaxes would do more harm than good.
@kiloalphaindia explained this in a comment:
If #2 would use
A::A(T)
we would end up withy
beeingA<A<int>>
. [...]
This is right. The A<A<int>>::A(A<int>)
constructor has an exact match in the parameter type. On the other hand, you're also right that A<int>::A(const A<int> &)
would in this case have been preferred instead.
But consider this alternative, where the function equivalent shows that A<A<int>>
would have been preferred if not for the copy deduction candidate:
template <typename T>
struct A {
A(T &&);
A(const A<T> &);
};
template <typename T> auto f(T &&) -> A<T>;
template <typename T> auto f(const A<T> &) -> A<T>;
int main() {
A x1(42); // A<int>
A y1 = std::move(x1); // A<int>
auto x2 = f(42); // A<int>
auto y2 = f(std::move(x2)); // A<A<int>>
}