Issue 4229: std::ranges::to with union return type (original) (raw)
This page is a snapshot from the LWG issues list, see the Library Active Issues List for more information and the meaning of NAD status.
4229. std::ranges::to with union return type
Section: 25.5.7.2 [range.utility.conv.to], 25.5.7.3 [range.utility.conv.adaptors] Status: NAD Submitter: Jiang An Opened: 2025-03-20 Last modified: 2025-12-10
Priority: Not Prioritized
View other active issues in [range.utility.conv.to].
View all other issues in [range.utility.conv.to].
View all issues with NAD status.
Discussion:
LWG 3847(i) made std::ranges::to require the return type (or the target type for the overload returning range adaptor closure object) to be a _cv_-unqualified class type. Although the term "class type" in core language specification also covers union types, implementations (libstdc++ and MSVC STL) tend to implement this part of the Mandates only with std::is_class_v, which rejects union types.
E.g. the following program is rejected by libstdc++ and MSVC STL (https://godbolt.org/z/MnsY4Tzen):
#include #include #include #include #include
template<class T, class A = std::allocator> union weird_vector { std::vector<T, A> vec_;
constexpr weird_vector() : vec_() {} constexpr weird_vector(const weird_vector& other) : vec_(other.vec_) {} constexpr weird_vector(weird_vector&& other) noexcept : vec_(std::move(other.vec_)) {}
template requires (!std::same_as<std::remove_cvref_t, weird_vector>) && (!std::same_as<std::remove_cvref_t, std::vector<T, A>>) && requires(U&& u) { std::vector<T, A>(std::forward(u)); } constexpr explicit weird_vector(U&& u) : vec_(std::forward(u)) {}
template<class T1, class T2, class... Ts> requires requires(T1&& t1, T2&& t2, Ts&&... ts) { std::vector<T, A>(std::forward(t1), std::forward(t2), std::forward(ts)...); } constexpr weird_vector(T1&& t1, T2&& t2, Ts&&... ts) : vec_(std::forward(t1), std::forward(t2), std::forward(ts)...) {}
constexpr weird_vector& operator=(const weird_vector& other) { vec_ = other.vec_; return *this; } constexpr weird_vector& operator=(weird_vector&& other) noexcept(std::is_nothrow_move_assignable_v<std::vector<T, A>>) { vec_ = std::move(other.vec_); return *this; }
constexpr
weird_vector() { vec_.vector(); } };int main() { int arr[]{42, 1729}; auto v [[maybe_unused]] = std::ranges::to<weird_vector>(arr); }
Although libc++ currently accepts this example, the acceptance seems to be a bug, because libc++ hasn't implemented the "class" part in the Mandates at all (llvm/llvm-project#132133).
It's unclear whether union types were intended to be accepted. Perhaps we should follow implementations' choices and reject them.
[2025-10-20; Reflector poll; Status changed: New → Tentatively NAD.]
"Those implementations have bugs and should be fixed."
There's no intrinsic reason why unions (with suitable constructors) should be rejected here."
[2025-12-10 Status changed: Tentatively NAD → NAD.]
Proposed resolution:
This wording is relative to N5008.
- Modify 25.5.7.2 [range.utility.conv.to] as indicated:
template<class C, input_range R, class... Args> requires (!view)
constexpr C to(R&& r, Args&&... args);-1- Mandates:
Cis a cv-unqualified non-union class type.[…]
- Modify 25.5.7.3 [range.utility.conv.adaptors] as indicated:
template<class C, class... Args> requires (!view)
constexpr auto to(Args&&... args);
template<template<class...> class C, class... Args>
constexpr auto to(Args&&... args);-1- Mandates: For the first overload,
Cis a cv-unqualified non-union class type.[…]