This page is a snapshot from the LWG issues list, see the Library Active Issues List for more information and the meaning of New status.
std::ranges::to
with union return typeSection: 25.5.7.2 [range.utility.conv.to], 25.5.7.3 [range.utility.conv.adaptors] Status: New Submitter: Jiang An Opened: 2025-03-20 Last modified: 2025-03-22
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 New 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.
#include <memory> #include <ranges> #include <type_traits> #include <utility> #include <vector> template<class T, class A = std::allocator<T>> 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<class U> requires (!std::same_as<std::remove_cvref_t<U>, weird_vector>) && (!std::same_as<std::remove_cvref_t<U>, std::vector<T, A>>) && requires(U&& u) { std::vector<T, A>(std::forward<U>(u)); } constexpr explicit weird_vector(U&& u) : vec_(std::forward<U>(u)) {} template<class T1, class T2, class... Ts> requires requires(T1&& t1, T2&& t2, Ts&&... ts) { std::vector<T, A>(std::forward<T1>(t1), std::forward<T2>(t2), std::forward<Ts>(ts)...); } constexpr weird_vector(T1&& t1, T2&& t2, Ts&&... ts) : vec_(std::forward<T1>(t1), std::forward<T2>(t2), std::forward<Ts>(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<int>>(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.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<C>) constexpr C to(R&& r, Args&&... args);-1- Mandates:
[…]C
is 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<C>) constexpr auto to(Args&&... args); template<template<class...> class C, class... Args> constexpr auto to(Args&&... args);-1- Mandates: For the first overload,
[…]C
is a cv-unqualified non-union class type.