Whenever a library clause uses CTAD or list-initialization, it’s wrong
Over the past few years, I’ve noticed a pattern in several LWG issues: In library clauses, CTAD and list-initialization are never helpful and are sometimes harmful, in the sense that they create LWG issues to triage and fix. I’d noticed for several years “this happens a lot,” but I’d never collected all my anecdotes into one list. I’m starting that list now. As Wikipedia says, “This list is incomplete”; please let me know if you see any I’ve missed.
The CTAD offender is almost always the copy deduction candidate; see
“Beware CTAD on reverse_iterator
” (2022-08-02)
for details. CTAD on a multi-argument constructor can pick the wrong candidate sometimes
too, but using CTAD to call a one-argument constructor is invariably a bug.
-
P0896R3: Copy deduction candidate. In R3,
reverse_view::begin()
was specified to returnreverse_iterator{ranges::end(base_)}
. Fixed in R4 by returningmake_reverse_iterator(ranges::end(base_))
(which incidentally eliminated the list-initialization too). -
LWG 3474: Copy deduction candidate.
views::join(E)
was defined as equivalent tojoin_view{E}
. This did the wrong thing whenE
itself was an instance ofjoin_view
. Fixed by getting rid of the CTAD (but not, yet, eliminating the list-initialization). -
LWG 4054: Copy deduction candidate.
views::repeat(E)
was defined as equivalent torepeat_view(E)
. This did the wrong thing whenE
itself was an instance ofrepeat_view
. Fixed by getting rid of the CTAD. -
LWG 4096: Copy deduction candidate.
views::iota(E)
was defined as equivalent toiota_view(E)
. This did the wrong thing whenE
itself was an instance ofiota_view
. Fixed by getting rid of the CTAD. -
LWG 4172: Avoiding list-initialization is the house style (and rightly so).
unique_lock{std::move(u)}.swap(*this)
was editorially amended intounique_lock(std::move(u)).swap(*this)
before application. (Notice that this isn’t CTAD; it’s a use of the injected-class-name](https://en.cppreference.com/w/cpp/language/injected-class-name.html).) -
LWG 4293: List-initialization.
span<T>::first
was specified to returnR{data(), Count}
. This did the wrong thing (since C++26) whenT
wasconst bool
, because then bothconst bool*
andsize_t
would be convertible tobool
, andspan<const bool>{data(), Count}
would be a span over an temporary array. Fixed by returningR(data(), Count)
.