This question is regarding: template<class...Types> class variant
:
According to variant.variant/3, a program instantiating variant
with no template arguments is ill-formed.
So far, so clear. Now I have a question regarding the converting constructor (template<class T> constexpr variant(T&& t) noexcept(see below)
):
variant.variant/variant.ctor-16.1 says that the converting constructor shall not participate in overload resolution unless:
sizeof...(Types) is nonzero
(... and some other requirements I do not care about for now).
My question is, when a variant
with no template arguments already makes my program ill-formed, why still care about whether my converting constructor participates in overload resolution or not?
Having a look at the MSVC and libstdc++ -implementation of variant
they actually have an enable_if_t<sizeof...(_Types) != 0>
at the declaration of the converting constructor. Why?
The clause "sizeof...(Types) is nonzero" was added to [variant.ctor]
as part of the paper: Some improvements to class template argument deduction integration into the standard library to allow variant
support as well.
Relevant Excerpts:
Enable variant support
The following code fails to compile
variant<int, double> v1(3); variant v2 = v1; // Ill-formed! <--THIS
As this natural code is useful and its failure is confusing, we propose that it be supported. Indeed, prior to the adoption of p0510r0 banning
variant<>
, the above code worked as expected sincevariant<>
occurs in some deduction guides in the overload set. As it is not clear that constructor template argument deduction was considered in adopting p0510r0, we would like to consider allowingvariant<>
not to produce a hard error in such cases.Wording (Emphasis added)
Change §23.7.3.1p16[variant.ctor]
as follows:
Remarks: This function shall not participate in overload resolution unlesssizeof...(Types)
is nonzero, unlessis_same_v<decay_t<T>, variant>
isfalse
, unlessdecay_t<T>
is neither a specialization ofin_place_type_t
nor a specialization ofin_place_index_t
, unlessis_constructible_v<Tj, T>
istrue
, and unless the expression FUN(std::forward(t)) (with FUN being the above-mentioned set of imaginary functions) is well formed.
So std::variant v2 = v1;
fails in compiler versions that do not take into account the added clause (like GCC 7.1. See DEMO) but succeeds on later versions (From GCC 7.2 onwards. See DEMO).
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With