T[N] als Containerelement
-
Das ging je früher (C++03) nicht. Kann jetzt aber beim Überfliegen auf Anhieb nichts finden, was ausdrücklich dagegen spricht. Hat sich hier etwas geändert (std::array ist klar aber sowieso ein spezieller Fall)?
vector<int[42], some_suitable_allocator_with_sane_array_construct_semantics> v; // ok?
-
<a href= schrieb:
containers.container.requirements">
X::value_type
Requires:T
is Erasable fromX
(see [container.requirements.general], below)<a href= schrieb:
p15.6">
T
is Erasable fromX
means that the following expression is well-formed:allocator_traits<A>::destroy(m, p)
In C++03 waren die Anforderungen höher:
value_type
muss CopyConstructible erfüllen, was natürlich von Arrays nicht gegeben ist. I.a. wurde der Text wahrscheinlich aufgeräumt und mehr entsprechende Operationen dem Allokator überlassen.
-
Arcoth schrieb:
<a href= schrieb:
containers.container.requirements">
X::value_type
Requires:T
is Erasable fromX
(see [container.requirements.general], below)<a href= schrieb:
p15.6">
T
is Erasable fromX
means that the following expression is well-formed:allocator_traits<A>::destroy(m, p)
In C++03 waren die Anforderungen höher:
value_type
muss CopyConstructible erfüllen, was natürlich von Arrays nicht gegeben ist. I.a. wurde der Text wahrscheinlich aufgeräumt und mehr entsprechende Operationen dem Allokator überlassen.hier sehe ich kein Problem, wenn der eigene Allokator destroy entsprechend implementiert.
#include <iostream> #include <new> #include <utility> #include <type_traits> #include <vector> template <typename T> struct my_allocate { using value_type = T; using is_always_equal = std::true_type; using elem_type = std::remove_all_extents_t<T>; using var_type = std::conditional_t<std::is_array_v<T>,elem_type[sizeof(T)/sizeof(elem_type)],T>; constexpr my_allocate() noexcept = default; constexpr my_allocate(const my_allocate&) noexcept = default; constexpr my_allocate(my_allocate&&) noexcept = default; constexpr my_allocate& operator=(const my_allocate&) noexcept = default; constexpr my_allocate& operator=(my_allocate&&) noexcept = default; template<class U> constexpr my_allocate(const my_allocate<U>&) noexcept {} ~my_allocate() noexcept = default; [[nodiscard]] T* allocate(size_t n) { return static_cast<T*>(::operator new(n * sizeof(T))); } void deallocate(T* p, size_t n) { ::operator delete(p, n); } template<typename... Args> void construct(T* p, Args&&... args) { ::new (static_cast<void*>(p)) var_type{std::forward<Args>(args)...}; } template<typename std::enable_if_t<std::is_array_v<T>, int> = 0, std::size_t... I> void construct(T* p, T&& arg, std::index_sequence<I...>) { ::new (static_cast<void*>(p)) var_type{std::move(std::launder(reinterpret_cast<elem_type*>(arg))[I])...}; } template<typename std::enable_if_t<std::is_array_v<T>, int> = 0, std::size_t... I> void construct(T* p, const T&& arg, std::index_sequence<I...>) { ::new (static_cast<void*>(p)) var_type{std::move(std::launder(reinterpret_cast<const elem_type*>(arg))[I])...}; } template<typename std::enable_if_t<std::is_array_v<T>, int> = 0, std::size_t... I> void construct(T* p, T& arg, std::index_sequence<I...>) { ::new (static_cast<void*>(p)) var_type{std::launder(reinterpret_cast<elem_type*>(arg))[I]...}; } template<typename std::enable_if_t<std::is_array_v<T>, int> = 0, std::size_t... I> void construct(T* p, const T& arg, std::index_sequence<I...>) { ::new (static_cast<void*>(p)) var_type{std::launder(reinterpret_cast<const elem_type*>(arg))[I]...}; } template<typename Arg, typename std::enable_if_t<std::is_same_v<T, std::remove_cv_t<std::remove_reference_t<Arg>>> && std::is_array_v<T>, int> = 0> void construct(T* p, Arg&& arg) { construct(p, std::forward<Arg>(arg), std::make_index_sequence<sizeof(T)/sizeof(elem_type)>{}); } void destroy(T* p) { if constexpr (std::is_array_v<T>) { auto q = std::launder(reinterpret_cast<elem_type*>(p)); for (auto n = sizeof(T)/sizeof(elem_type); n--;) q[n].~elem_type(); } else p->~T(); } }; template <typename T, typename U> constexpr bool operator==(const my_allocate<T>&, const my_allocate<U>&) { return true; } template <typename T, typename U> constexpr bool operator!=(const my_allocate<T>&, const my_allocate<U>&) { return false; } template <typename T, std::size_t N> std::ostream& operator<<(std::ostream& s, const T(&a)[N]) { s << '('; for (auto n = N; --n; ) s << a[N-1-n] << ','; s << a[N-1] << ')'; return s; } int main() { std::vector<int[2][2], my_allocate<int[2][2]>> x; x.emplace_back(1,2,3); x.emplace_back(x.front()); std::cout << x.back() << '\n'; }
-
@camper: Das ist doch genau mein Punkt. Ich habe aufgezeigt, warum die Regeln für C++11, die statt CopyConstructible nur Erasable fordern, Arrays nicht mehr ausschließen.