From 127a6fbecf14684b580986ab04d4cfcfe6650a2f Mon Sep 17 00:00:00 2001 From: Arthur O'Dwyer Date: Sat, 21 Oct 2023 13:23:37 -0400 Subject: [PATCH] [inplace_vector] Fix iter-pair ctor for non-assignable value_type --- include/sg14/inplace_vector.h | 28 +++++++++++- test/inplace_vector_test.cpp | 82 +++++++++++++++++++++++++++++++++++ 2 files changed, 108 insertions(+), 2 deletions(-) diff --git a/include/sg14/inplace_vector.h b/include/sg14/inplace_vector.h index f827c8c..6215fe5 100644 --- a/include/sg14/inplace_vector.h +++ b/include/sg14/inplace_vector.h @@ -239,7 +239,20 @@ class inplace_vector : ipvbase_assignable, ipvbase_t { constexpr explicit inplace_vector(size_t n, const value_type& value) { assign(n, value); } template::iterator_category>, int> = 0> - constexpr explicit inplace_vector(It first, It last) { assign(first, last); } + constexpr explicit inplace_vector(It first, It last) { + if constexpr (std::is_base_of_v::iterator_category>) { + size_t n = last - first; + if (n > N) { + SG14_INPLACE_VECTOR_THROW(std::bad_alloc()); + } + std::uninitialized_copy_n(first, n, data()); + set_size_(n); + } else { + for (; first != last; ++first) { + emplace_back(*first); + } + } + } constexpr void assign(std::initializer_list il) { assign(il.begin(), il.end()); } @@ -284,7 +297,18 @@ class inplace_vector : ipvbase_assignable, ipvbase_t { template requires std::convertible_to, value_type> constexpr explicit inplace_vector(std::from_range_t, R&& rg) { - assign_range(rg); + if constexpr (std::ranges::sized_range) { + size_t n = std::ranges::size(rg); + if (n > N) { + SG14_INPLACE_VECTOR_THROW(std::bad_alloc()); + } + std::ranges::uninitialized_copy_n(std::ranges::begin(rg), n, data(), std::unreachable_sentinel); + set_size_(n); + } else { + for (auto&& e : rg) { + emplace_back(decltype(e)(e)); + } + } } template diff --git a/test/inplace_vector_test.cpp b/test/inplace_vector_test.cpp index 36679a7..3711909 100644 --- a/test/inplace_vector_test.cpp +++ b/test/inplace_vector_test.cpp @@ -426,6 +426,28 @@ TEST(inplace_vector, ConstructorsThrow) } } +TEST(inplace_vector, IterPairNonAssignable) +{ + struct NA { + std::string s_; + NA(const char *s) : s_(s) {} + NA(NA&&) = delete; + NA& operator=(NA&&) = delete; + bool operator==(const char *s) const { return s_ == s; } + }; + const char *a[] = {"1", "2", "3"}; + { + auto v = sg14::inplace_vector(a, a+3); + EXPECT_EQ(v, Seq("1", "2", "3")); + } +#if defined(__cpp_lib_ranges_to_container) + { + auto v = sg14::inplace_vector(std::from_range, a); + EXPECT_EQ(v, Seq("1", "2", "3")); + } +#endif +} + TEST(inplace_vector, Copying) { { @@ -809,6 +831,66 @@ TEST(inplace_vector, InsertMulti) } } +TEST(inplace_vector, Assign) +{ + { + using V = sg14::inplace_vector; + V v; + int a[] = {1, 2, 3, 4, 5, 6}; + v.assign(a, a+2); + static_assert(std::is_same_v); + EXPECT_EQ(v, (V{1, 2})); + v.assign(a+2, a+6); + EXPECT_EQ(v, (V{3, 4, 5, 6})); + v.assign(a, a+2); + EXPECT_EQ(v, (V{1, 2})); + ASSERT_THROW(v.assign(a, a+6), std::bad_alloc); + EXPECT_LE(v.size(), 5u); + } + { + using V = sg14::inplace_vector; + V v; + const char *a[] = {"1", "2", "3", "4", "5", "6"}; + v.assign(a, a+2); + EXPECT_EQ(v, Seq("1", "2")); + v.assign(a+2, a+6); + EXPECT_EQ(v, Seq("3", "4", "5", "6")); + v.assign(a, a+2); + EXPECT_EQ(v, Seq("1", "2")); + ASSERT_THROW(v.assign(a, a+6), std::bad_alloc); + EXPECT_LE(v.size(), 5u); + } + { + using V = sg14::inplace_vector; + V v; + auto iss = std::istringstream("1 2"); + v.assign(std::istream_iterator(iss), {}); + EXPECT_EQ(v, (V{1, 2})); + iss = std::istringstream("4 5 6 7"); + v.assign(std::istream_iterator(iss), {}); + EXPECT_EQ(v, (V{4, 5, 6, 7})); + iss = std::istringstream("1 2"); + v.assign(std::istream_iterator(iss), {}); + EXPECT_EQ(v, (V{1, 2})); + iss = std::istringstream("1 2 3 4 5 6"); + ASSERT_THROW(v.assign(std::istream_iterator(iss), {}), std::bad_alloc); + EXPECT_LE(v.size(), 5u); + } + { + using V = sg14::inplace_vector; + const char *a[] = {"1", "2", "3", "4", "5", "6"}; + V v; + v.assign(a, a+2); + EXPECT_EQ(v, Seq("1", "2")); + v.assign(a+2, a+6); + EXPECT_EQ(v, Seq("3", "4", "5", "6")); + v.assign(a, a+2); + EXPECT_EQ(v, Seq("1", "2")); + ASSERT_THROW(v.assign(a, a+6), std::bad_alloc); + EXPECT_LE(v.size(), 5u); + } +} + TEST(inplace_vector, AssignRange) { #if defined(__cpp_lib_ranges_to_container)