[libc++][ranges] Implement ranges::uninitialized_default_construct{,_n}.

Defined in [`specialized.algorithms`](wg21.link/specialized.algorithms).

Also:
- refactor the existing non-range implementation so that most of it
  can be shared between the range-based and non-range-based algorithms;
- remove an existing test for the non-range version of
  `uninitialized_default_construct{,_n}` that likely triggered undefined
  behavior (it read the values of built-ins after default-initializing
  them, essentially reading uninitialized memory).

Reviewed By: #libc, Quuxplusone, ldionne

Differential Revision: https://reviews.llvm.org/D115315
This commit is contained in:
Konstantin Varlamov 2021-12-14 14:11:37 -08:00
parent 624f12d34f
commit 3f630cff65
17 changed files with 556 additions and 75 deletions

View File

@ -12,14 +12,14 @@ Section,Description,Dependencies,Assignee,Complete
| `iter_difference_t <https://llvm.org/D99863>`_",,Christopher Di Bella,✅
`[iterator.traits] <http://wg21.link/iterator.traits>`_,`Updates to iterator_traits <https://llvm.org/D99855>`_,"| indirectly_readable_traits
| incrementable_traits",Christopher Di Bella,✅
`[special.mem.concepts] <http://wg21.link/special.mem.concepts>`_,"| *no-throw-input-iterator*
| *no-throw-sentinel-for*
| *no-throw-input-range*
| *no-throw-forward-iterator*
| *no-throw-forward-range*","| [iterator.concepts]
`[special.mem.concepts] <http://wg21.link/special.mem.concepts>`_,"| *nothrow-input-iterator*
| *nothrow-sentinel-for*
| *nothrow-input-range*
| *nothrow-forward-iterator*
| *nothrow-forward-range*","| [iterator.concepts]
| [range.refinements]",Konstantin Varlamov,✅
`[specialized.algorithms] <http://wg21.link/specialized.algorithms>`_,"| ranges::uninitialized_default_construct
| ranges::uninitialized_default_construct_n
`[specialized.algorithms] <http://wg21.link/specialized.algorithms>`_,"| `ranges::uninitialized_default_construct <https://llvm.org/D115315>`
| `ranges::uninitialized_default_construct_n <https://llvm.org/D115315>`
| ranges::uninitialized_value_construct
| ranges::uninitialized_value_construct_n
| ranges::uninitialized_copy
@ -31,7 +31,7 @@ Section,Description,Dependencies,Assignee,Complete
| ranges::construct_at
| ranges::destroy
| ranges::destroy_at
| ranges::destroy_n",[special.mem.concepts],Konstantin Varlamov,Not started
| ranges::destroy_n",[special.mem.concepts],Konstantin Varlamov,In progress
[strings],Adds begin/end and updates const_iterator.,[iterator.concepts],Unassigned,Not started
[views.span],Same as [strings],[iterator.concepts],Unassigned,Not started
`[iterator.cust.move] <http://wg21.link/iterator.cust.move>`_,`ranges::iter_move <https://llvm.org/D99873>`_,,Zoe Carver,✅

1 Section Description Dependencies Assignee Complete
12 `[iterator.cust.swap] <http://wg21.link/iterator.cust.swap>`_ `ranges::iter_swap <https://llvm.org/D102809>`_ iter_value_t Zoe Carver
13 `[iterator.concepts] <http://wg21.link/iterator.concepts>`_ | `indirectly_readable <https://llvm.org/D100073>`_ | `indirectly_writable <https://llvm.org/D100073>`_ | `weakly_incrementable <https://llvm.org/D100080>`_ | `incrementable <https://llvm.org/D100080>`_ | `input_or_output_iterator <https://llvm.org/D100160>`_ | `sentinel_for <https://llvm.org/D100160>`_ | `sized_sentinel_for <https://llvm.org/D101371>`_ | `input_iterator <https://llvm.org/D100271>`_ | `output_iterator <https://llvm.org/D106704>`_ | `forward_iterator <https://llvm.org/D100275>`_ | `bidirectional_iterator <https://llvm.org/D100278>`_ | `random_access_iterator <https://llvm.org/D101316>`_ | `contiguous_iterator <https://llvm.org/D101396>`_ Various
14 `[indirectcallable.indirectinvocable] <http://wg21.link/indirectcallable.indirectinvocable>`_ | `indirectly_unary_invocable <https://llvm.org/D101277>`_ | `indirectly_regular_unary_invocable <https://llvm.org/D101277>`_ | `indirectly_unary_predicate <https://llvm.org/D101277>`_ | `indirectly_binary_predicate <https://llvm.org/D101277>`_ | `indirectly_equivalence_relation <https://llvm.org/D101277>`_ | `indirectly_strict_weak_order <https://llvm.org/D101277>`_ [readable.traits] Louis Dionne
15 `[projected] <http://wg21.link/projected>`_ `ranges::projected <https://llvm.org/D101277>`_ [iterator.concepts] Louis Dionne
16 `[common.alg.req] <http://wg21.link/common.alg.req>`_: pt. 1 | `indirectly_movable <https://llvm.org/D102639>`_ | `indirectly_movable_storable <https://llvm.org/D102639>`_ | indirectly_copyable | indirectly_copyable_storable [iterator.concepts] Zoe Carver In progress
17 [common.alg.req]: pt. 2 indirectly_swappable | [iterator.concepts] | [iterator.cust.swap] Zoe Carver
18 [common.alg.req]: pt. 3 indirectly_comparable [projected] Louis Dionne Not started
19 [common.alg.req]: pt. 4 | permutable | mergeable | sortable [iterator.concepts] Unassigned Not started
20 [std.iterator.tags] [iterator.traits] Unassigned Not started
21 `[range.iter.ops] <http://wg21.link/range.iter.ops>`_ | `ranges::advance <https://llvm.org/D101922>`_ | `ranges::distance <https://llvm.org/D102789>`_ | `ranges::next <https://llvm.org/D102563>`_ | `ranges::prev <https://llvm.org/D102564>`_ [iterator.concepts] Christopher Di Bella In progress
22 [predef.iterators] Updates to predefined iterators. | [iterator.concepts] | [iterator.cust.swap] | [iterator.cust.move] Unassigned Not started
23 [move.sentinel] [predef.iterators] Unassigned Not started
24 [common.iterator] | [iterator.concepts] | [iterator.cust.swap] | [iterator.cust.move] Zoe Carver
25 [default.sentinels] std::default_sentinel_t. No dependencies Zoe Carver
31 `[range.view] <http://wg21.link/range.view>`_ | `ranges::enable_view <https://llvm.org/D101547>`_ | `ranges::view_base <https://llvm.org/D101547>`_ | `ranges::view <https://llvm.org/D101547>`_ [range.range] Louis Dionne
32 `[range.refinements] <http://wg21.link/range.refinements>`_ | ranges::output_range | `ranges::input_range <https://llvm.org/D100271>`_ | `ranges::forward_range: `D100275 <https://llvm.org/D100275>`_ | `ranges::bidirectional_range <https://llvm.org/D100278>`_ | `ranges::random_access_range <https://llvm.org/D101316>`_ | ranges::contiguous_range | `ranges::common_range <https://llvm.org/D100269>`_ [range.range] Christopher Di Bella
33 `[range.refinements]`_ `ranges::viewable_range <https://llvm.org/D105816>`_ [range.range] Louis Dionne
34 `[range.utility.helpers] <http://wg21.link/range.utility.helpers>`_ | *simple-view* | *has-arrow* | *not-same-as* | [range.range] | [iterator.concept.input] Zoe Carver
35 `[view.interface] <http://wg21.link/view.interface>`_ `ranges::view_interface <https://llvm.org/D101737>`_ | [ranges.range] | [range.view] | [range.iterator.op.prev] | [range.refinements] Zoe Carver
36 `[range.subrange] <http://wg21.link/range.subrange>`_ `ranges::subrange <https://llvm.org/D102006>`_ [view.interface] Zoe Carver
37 `[range.dangling] <http://wg21.link/range.dangling>`_ | ranges::dangling | ranges::borrowed_iterator_t | ranges::borrowed_subrange_t | [range.range] | [range.subrange] Christopher Di Bella

View File

@ -231,6 +231,7 @@ set(files
__memory/concepts.h
__memory/construct_at.h
__memory/pointer_traits.h
__memory/ranges_uninitialized_algorithms.h
__memory/raw_storage_iterator.h
__memory/shared_ptr.h
__memory/temporary_buffer.h

View File

@ -69,6 +69,7 @@ void advance(_InputIter& __i, _Distance __orig_n) {
namespace ranges {
// [range.iter.op.advance]
// TODO(varconst): rename `__advance_fn` to `__fn`.
struct __advance_fn final : private __function_like {
private:
template <class _Tp>

View File

@ -39,6 +39,7 @@ inline _LIBCPP_INLINE_VISIBILITY _LIBCPP_CONSTEXPR_AFTER_CXX14
#if !defined(_LIBCPP_HAS_NO_RANGES)
namespace ranges {
// TODO(varconst): rename `__next_fn` to `__fn`.
struct __next_fn final : private __function_like {
_LIBCPP_HIDE_FROM_ABI
constexpr explicit __next_fn(__tag __x) noexcept : __function_like(__x) {}

View File

@ -38,6 +38,7 @@ inline _LIBCPP_INLINE_VISIBILITY _LIBCPP_CONSTEXPR_AFTER_CXX14
#if !defined(_LIBCPP_HAS_NO_RANGES)
namespace ranges {
// TODO(varconst): rename `__prev_fn` to `__fn`.
struct __prev_fn final : private __function_like {
_LIBCPP_HIDE_FROM_ABI
constexpr explicit __prev_fn(__tag __x) noexcept : __function_like(__x) {}

View File

@ -0,0 +1,97 @@
// -*- C++ -*-
//===----------------------------------------------------------------------===//
//
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
// See https://llvm.org/LICENSE.txt for license information.
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//
#ifndef _LIBCPP___MEMORY_RANGES_UNINITIALIZED_ALGORITHMS_H
#define _LIBCPP___MEMORY_RANGES_UNINITIALIZED_ALGORITHMS_H
#include <__concepts/constructible.h>
#include <__config>
#include <__function_like.h>
#include <__iterator/incrementable_traits.h>
#include <__iterator/iterator_traits.h>
#include <__iterator/readable_traits.h>
#include <__memory/concepts.h>
#include <__memory/uninitialized_algorithms.h>
#include <__ranges/access.h>
#include <__ranges/concepts.h>
#include <__ranges/dangling.h>
#include <type_traits>
#if !defined(_LIBCPP_HAS_NO_PRAGMA_SYSTEM_HEADER)
#pragma GCC system_header
#endif
_LIBCPP_BEGIN_NAMESPACE_STD
#if !defined(_LIBCPP_HAS_NO_RANGES)
namespace ranges {
// uninitialized_default_construct
namespace __uninitialized_default_construct {
struct __fn final : private __function_like {
constexpr explicit __fn(__tag __x) noexcept : __function_like(__x) {}
template <__nothrow_forward_iterator _ForwardIterator,
__nothrow_sentinel_for<_ForwardIterator> _Sentinel>
requires default_initializable<iter_value_t<_ForwardIterator>>
_ForwardIterator operator()(_ForwardIterator __first, _Sentinel __last) const {
using _ValueType = remove_reference_t<iter_reference_t<_ForwardIterator>>;
return _VSTD::__uninitialized_default_construct<_ValueType>(__first, __last);
}
template <__nothrow_forward_range _ForwardRange>
requires default_initializable<range_value_t<_ForwardRange>>
borrowed_iterator_t<_ForwardRange> operator()(_ForwardRange&& __range) const {
return (*this)(ranges::begin(__range), ranges::end(__range));
}
};
} // namespace __uninitialized_default_construct_ns
inline namespace __cpo {
inline constexpr auto uninitialized_default_construct =
__uninitialized_default_construct::__fn(__function_like::__tag());
} // namespace __cpo
// uninitialized_default_construct_n
namespace __uninitialized_default_construct_n {
struct __fn final : private __function_like {
constexpr explicit __fn(__tag __x) noexcept :
__function_like(__x) {}
template <__nothrow_forward_iterator _ForwardIterator>
requires default_initializable<iter_value_t<_ForwardIterator>>
_ForwardIterator operator()(_ForwardIterator __first,
iter_difference_t<_ForwardIterator> __n) const {
using _ValueType = remove_reference_t<iter_reference_t<_ForwardIterator>>;
return _VSTD::__uninitialized_default_construct_n<_ValueType>(__first, __n);
}
};
} // namespace __uninitialized_default_construct_n_ns
inline namespace __cpo {
inline constexpr auto uninitialized_default_construct_n =
__uninitialized_default_construct_n::__fn(__function_like::__tag());
} // namespace __cpo
} // namespace ranges
#endif // !defined(_LIBCPP_HAS_NO_RANGES)
_LIBCPP_END_NAMESPACE_STD
#endif // _LIBCPP___MEMORY_RANGES_UNINITIALIZED_ALGORITHMS_H

View File

@ -119,43 +119,61 @@ uninitialized_fill_n(_ForwardIterator __f, _Size __n, const _Tp& __x)
#if _LIBCPP_STD_VER > 14
template <class _ForwardIterator>
inline _LIBCPP_INLINE_VISIBILITY
void uninitialized_default_construct(_ForwardIterator __first, _ForwardIterator __last) {
using _Vt = typename iterator_traits<_ForwardIterator>::value_type;
// uninitialized_default_construct
template <class _ValueType, class _ForwardIterator, class _Sentinel>
inline _LIBCPP_HIDE_FROM_ABI
_ForwardIterator __uninitialized_default_construct(_ForwardIterator __first, _Sentinel __last) {
auto __idx = __first;
#ifndef _LIBCPP_NO_EXCEPTIONS
try {
#endif
for (; __idx != __last; ++__idx)
::new ((void*)_VSTD::addressof(*__idx)) _Vt;
::new ((void*)_VSTD::addressof(*__idx)) _ValueType;
#ifndef _LIBCPP_NO_EXCEPTIONS
} catch (...) {
_VSTD::destroy(__first, __idx);
throw;
}
#endif
return __idx;
}
template <class _ForwardIterator, class _Size>
template <class _ForwardIterator>
inline _LIBCPP_INLINE_VISIBILITY
_ForwardIterator uninitialized_default_construct_n(_ForwardIterator __first, _Size __n) {
using _Vt = typename iterator_traits<_ForwardIterator>::value_type;
void uninitialized_default_construct(_ForwardIterator __first, _ForwardIterator __last) {
using _ValueType = typename iterator_traits<_ForwardIterator>::value_type;
(void)_VSTD::__uninitialized_default_construct<_ValueType>(__first, __last);
}
// uninitialized_default_construct_n
template <class _ValueType, class _ForwardIterator, class _Size>
inline _LIBCPP_HIDE_FROM_ABI
_ForwardIterator __uninitialized_default_construct_n(_ForwardIterator __first, _Size __n) {
auto __idx = __first;
#ifndef _LIBCPP_NO_EXCEPTIONS
try {
#endif
for (; __n > 0; ++__idx, (void) --__n)
::new ((void*)_VSTD::addressof(*__idx)) _Vt;
return __idx;
::new ((void*)_VSTD::addressof(*__idx)) _ValueType;
#ifndef _LIBCPP_NO_EXCEPTIONS
} catch (...) {
_VSTD::destroy(__first, __idx);
throw;
}
#endif
return __idx;
}
template <class _ForwardIterator, class _Size>
inline _LIBCPP_INLINE_VISIBILITY
_ForwardIterator uninitialized_default_construct_n(_ForwardIterator __first, _Size __n) {
using _ValueType = typename iterator_traits<_ForwardIterator>::value_type;
return _VSTD::__uninitialized_default_construct_n<_ValueType>(__first, __n);
}
template <class _ForwardIterator>
inline _LIBCPP_INLINE_VISIBILITY

View File

@ -219,9 +219,21 @@ template <class ForwardIterator, class Size>
template <class ForwardIterator>
void uninitialized_default_construct(ForwardIterator first, ForwardIterator last);
template <nothrow-forward-iterator ForwardIterator, nothrow-sentinel-for<ForwardIterator> Sentinel>
requires default_initializable<iter_value_t<ForwardIterator>>
ForwardIterator ranges::uninitialized_default_construct(ForwardIterator first, Sentinel last); // since C++20
template <nothrow-forward-range ForwardRange>
requires default_initializable<range_value_t<ForwardRange>>
borrowed_iterator_t<ForwardRange> ranges::uninitialized_default_construct(ForwardRange&& r); // since C++20
template <class ForwardIterator, class Size>
ForwardIterator uninitialized_default_construct_n(ForwardIterator first, Size n);
template <nothrow-forward-iterator ForwardIterator>
requires default_initializable<iter_value_t<ForwardIterator>>
ForwardIterator ranges::uninitialized_default_construct_n(ForwardIterator first, iter_difference_t<ForwardIterator> n); // since C++20
template <class Y> struct auto_ptr_ref {}; // deprecated in C++11, removed in C++17
template<class X>
@ -672,6 +684,7 @@ void* align(size_t alignment, size_t size, void*& ptr, size_t& space);
#include <__memory/concepts.h>
#include <__memory/construct_at.h>
#include <__memory/pointer_traits.h>
#include <__memory/ranges_uninitialized_algorithms.h>
#include <__memory/raw_storage_iterator.h>
#include <__memory/shared_ptr.h>
#include <__memory/temporary_buffer.h>

View File

@ -630,22 +630,26 @@ module std [system] {
export *
module __memory {
module addressof { private header "__memory/addressof.h" }
module allocation_guard { private header "__memory/allocation_guard.h" }
module allocator { private header "__memory/allocator.h" }
module allocator_arg_t { private header "__memory/allocator_arg_t.h" }
module allocator_traits { private header "__memory/allocator_traits.h" }
module auto_ptr { private header "__memory/auto_ptr.h" }
module compressed_pair { private header "__memory/compressed_pair.h" }
module concepts { private header "__memory/concepts.h" }
module construct_at { private header "__memory/construct_at.h" }
module pointer_traits { private header "__memory/pointer_traits.h" }
module raw_storage_iterator { private header "__memory/raw_storage_iterator.h" }
module shared_ptr { private header "__memory/shared_ptr.h" }
module temporary_buffer { private header "__memory/temporary_buffer.h" }
module uninitialized_algorithms { private header "__memory/uninitialized_algorithms.h" }
module unique_ptr { private header "__memory/unique_ptr.h" }
module uses_allocator { private header "__memory/uses_allocator.h" }
module addressof { private header "__memory/addressof.h" }
module allocation_guard { private header "__memory/allocation_guard.h" }
module allocator { private header "__memory/allocator.h" }
module allocator_arg_t { private header "__memory/allocator_arg_t.h" }
module allocator_traits { private header "__memory/allocator_traits.h" }
module auto_ptr { private header "__memory/auto_ptr.h" }
module compressed_pair { private header "__memory/compressed_pair.h" }
module concepts { private header "__memory/concepts.h" }
module construct_at { private header "__memory/construct_at.h" }
module pointer_traits { private header "__memory/pointer_traits.h" }
module ranges_uninitialized_algorithms {
private header "__memory/ranges_uninitialized_algorithms.h"
export __function_like
}
module raw_storage_iterator { private header "__memory/raw_storage_iterator.h" }
module shared_ptr { private header "__memory/shared_ptr.h" }
module temporary_buffer { private header "__memory/temporary_buffer.h" }
module uninitialized_algorithms { private header "__memory/uninitialized_algorithms.h" }
module unique_ptr { private header "__memory/unique_ptr.h" }
module uses_allocator { private header "__memory/uses_allocator.h" }
}
}
module mutex {
@ -755,7 +759,10 @@ module std [system] {
module common_view { private header "__ranges/common_view.h" }
module concepts { private header "__ranges/concepts.h" }
module copyable_box { private header "__ranges/copyable_box.h" }
module counted { private header "__ranges/counted.h" }
module counted {
private header "__ranges/counted.h"
export span
}
module dangling { private header "__ranges/dangling.h" }
module data { private header "__ranges/data.h" }
module drop_view { private header "__ranges/drop_view.h" }

View File

@ -0,0 +1,15 @@
//===----------------------------------------------------------------------===//
//
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
// See https://llvm.org/LICENSE.txt for license information.
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//
// REQUIRES: modules-build
// WARNING: This test was generated by 'generate_private_header_tests.py'
// and should not be edited manually.
// expected-error@*:* {{use of private header from outside its module: '__memory/ranges_uninitialized_algorithms.h'}}
#include <__memory/ranges_uninitialized_algorithms.h>

View File

@ -81,6 +81,7 @@ void advance(forward_iterator<I>&, std::ptrdiff_t, forward_iterator<I>) {
}
} // namespace test
// TODO(varconst): simply check that `advance` is a variable and not a function.
// When found by unqualified ([basic.lookup.unqual]) name lookup for the postfix-expression in a
// function call ([expr.call]), they inhibit argument-dependent name lookup.
void adl_inhibition() {

View File

@ -88,6 +88,7 @@ void next(forward_iterator<I>, std::ptrdiff_t, forward_iterator<I>) {
}
} // namespace test
// TODO(varconst): simply check that `next` is a variable and not a function.
// When found by unqualified ([basic.lookup.unqual]) name lookup for the postfix-expression in a
// function call ([expr.call]), they inhibit argument-dependent name lookup.
void adl_inhibition() {

View File

@ -83,6 +83,7 @@ void prev(bidirectional_iterator<I>, std::ptrdiff_t, bidirectional_iterator<I>)
}
} // namespace test
// TODO(varconst): simply check that `prev` is a variable and not a function.
// When found by unqualified ([basic.lookup.unqual]) name lookup for the postfix-expression in a
// function call ([expr.call]), they inhibit argument-dependent name lookup.
void adl_inhibition() {

View File

@ -0,0 +1,234 @@
//===----------------------------------------------------------------------===//
//
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
// See https://llvm.org/LICENSE.txt for license information.
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//
// UNSUPPORTED: c++03, c++11, c++14, c++17
// UNSUPPORTED: libcpp-no-concepts, libcpp-has-no-incomplete-ranges
// <memory>
// template <nothrow-forward-iterator ForwardIterator, nothrow-sentinel-for<ForwardIterator> Sentinel>
// requires default_initializable<iter_value_t<ForwardIterator>>
// ForwardIterator ranges::uninitialized_default_construct(ForwardIterator first, Sentinel last);
//
// template <nothrow-forward-range ForwardRange>
// requires default_initializable<range_value_t<ForwardRange>>
// borrowed_iterator_t<ForwardRange> ranges::uninitialized_default_construct(ForwardRange&& range);
#include <cassert>
#include <iterator>
#include <memory>
#include <ranges>
#include <type_traits>
#include "test_macros.h"
#include "test_iterators.h"
struct Counted {
static int current_objects;
static int total_objects;
static int throw_on;
explicit Counted() {
if (throw_on == total_objects) {
TEST_THROW(1);
}
++current_objects;
++total_objects;
}
~Counted() { --current_objects; }
static void reset() {
current_objects = total_objects = 0;
throw_on = -1;
}
Counted(Counted const&) = delete;
friend void operator&(Counted) = delete;
};
int Counted::current_objects = 0;
int Counted::total_objects = 0;
int Counted::throw_on = -1;
template <typename T, int N>
struct Buffer {
alignas(T) char buffer[sizeof(T) * N] = {};
T* begin() { return reinterpret_cast<T*>(buffer); }
T* end() { return begin() + N; }
const T* cbegin() const { return reinterpret_cast<const T*>(buffer); }
const T* cend() const { return cbegin() + N; }
};
// Because this is a variable and not a function, it's guaranteed that ADL won't be used. However,
// implementations are allowed to use a different mechanism to achieve this effect, so this check is
// libc++-specific.
LIBCPP_STATIC_ASSERT(std::is_class_v<decltype(std::ranges::uninitialized_default_construct)>);
struct NotDefaultCtrable { NotDefaultCtrable() = delete; };
static_assert(!std::is_invocable_v<decltype(std::ranges::uninitialized_default_construct),
NotDefaultCtrable*, NotDefaultCtrable*>);
int main(int, char**) {
// An empty range -- no default constructors should be invoked.
{
Buffer<Counted, 1> buf;
std::ranges::uninitialized_default_construct(buf.begin(), buf.begin());
assert(Counted::current_objects == 0);
assert(Counted::total_objects == 0);
std::ranges::uninitialized_default_construct(std::ranges::empty_view<Counted>());
assert(Counted::current_objects == 0);
assert(Counted::total_objects == 0);
forward_iterator<Counted*> it(buf.begin());
auto range = std::ranges::subrange(it, sentinel_wrapper<forward_iterator<Counted*>>(it));
std::ranges::uninitialized_default_construct(range.begin(), range.end());
assert(Counted::current_objects == 0);
assert(Counted::total_objects == 0);
std::ranges::uninitialized_default_construct(range);
assert(Counted::current_objects == 0);
assert(Counted::total_objects == 0);
}
// A range containing several objects, (iter, sentinel) overload.
{
constexpr int N = 5;
Buffer<Counted, 5> buf;
std::ranges::uninitialized_default_construct(buf.begin(), buf.end());
assert(Counted::current_objects == N);
assert(Counted::total_objects == N);
std::destroy(buf.begin(), buf.end());
assert(Counted::current_objects == 0);
assert(Counted::total_objects == N);
Counted::reset();
}
// A range containing several objects, (range) overload.
{
constexpr int N = 5;
Buffer<Counted, N> buf;
auto range = std::ranges::subrange(buf.begin(), buf.end());
std::ranges::uninitialized_default_construct(range);
assert(Counted::current_objects == N);
assert(Counted::total_objects == N);
std::destroy(buf.begin(), buf.end());
assert(Counted::current_objects == 0);
assert(Counted::total_objects == N);
Counted::reset();
}
// Using `counted_iterator`.
{
constexpr int N = 3;
Buffer<Counted, 5> buf;
std::ranges::uninitialized_default_construct(
std::counted_iterator(buf.begin(), N), std::default_sentinel);
assert(Counted::current_objects == N);
assert(Counted::total_objects == N);
std::destroy(buf.begin(), buf.begin() + N);
Counted::reset();
}
// Using `views::counted`.
{
constexpr int N = 3;
Buffer<Counted, 5> buf;
auto counted_range = std::views::counted(buf.begin(), N);
std::ranges::uninitialized_default_construct(counted_range);
assert(Counted::current_objects == N);
assert(Counted::total_objects == N);
std::destroy(buf.begin(), buf.begin() + N);
Counted::reset();
}
// Using `reverse_view`.
{
constexpr int N = 3;
Buffer<Counted, 5> buf;
auto range = std::ranges::subrange(buf.begin(), buf.begin() + N);
std::ranges::uninitialized_default_construct(std::ranges::reverse_view(range));
assert(Counted::current_objects == N);
assert(Counted::total_objects == N);
std::destroy(buf.begin(), buf.begin() + N);
Counted::reset();
}
// An exception is thrown while objects are being created -- the existing objects should stay
// valid. (iterator, sentinel) overload.
#ifndef TEST_HAS_NO_EXCEPTIONS
{
Buffer<Counted, 5> buf;
Counted::throw_on = 3; // When constructing the fourth object (counting from one).
try {
std::ranges::uninitialized_default_construct(buf.begin(), buf.end());
} catch(...) {}
assert(Counted::current_objects == 0);
assert(Counted::total_objects == 3);
std::destroy(buf.begin(), buf.begin() + Counted::total_objects);
Counted::reset();
}
// An exception is thrown while objects are being created -- the existing objects should stay
// valid. (range) overload.
{
Buffer<Counted, 5> buf;
Counted::throw_on = 3; // When constructing the fourth object.
try {
auto range = std::ranges::subrange(buf.begin(), buf.end());
std::ranges::uninitialized_default_construct(range);
} catch(...) {}
assert(Counted::current_objects == 0);
assert(Counted::total_objects == 3);
std::destroy(buf.begin(), buf.begin() + Counted::total_objects);
Counted::reset();
}
#endif // TEST_HAS_NO_EXCEPTIONS
// Works with const iterators, (iter, sentinel) overload.
{
constexpr int N = 5;
Buffer<Counted, N> buf;
std::ranges::uninitialized_default_construct(buf.cbegin(), buf.cend());
assert(Counted::current_objects == N);
assert(Counted::total_objects == N);
std::destroy(buf.begin(), buf.end());
Counted::reset();
}
// Works with const iterators, (range) overload.
{
constexpr int N = 5;
Buffer<Counted, N> buf;
auto range = std::ranges::subrange(buf.cbegin(), buf.cend());
std::ranges::uninitialized_default_construct(range);
assert(Counted::current_objects == N);
assert(Counted::total_objects == N);
std::destroy(buf.begin(), buf.end());
Counted::reset();
}
return 0;
}

View File

@ -0,0 +1,129 @@
//===----------------------------------------------------------------------===//
//
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
// See https://llvm.org/LICENSE.txt for license information.
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//
// UNSUPPORTED: c++03, c++11, c++14, c++17
// UNSUPPORTED: libcpp-no-concepts, libcpp-has-no-incomplete-ranges
// <memory>
// template <nothrow-forward-iterator ForwardIterator>
// requires default_initializable<iter_value_t<ForwardIterator>>
// ForwardIterator ranges::uninitialized_default_construct_n(ForwardIterator first,
// iter_difference_t<ForwardIterator> n);
#include <cassert>
#include <memory>
#include <ranges>
#include "test_macros.h"
#include "test_iterators.h"
struct Counted {
static int current_objects;
static int total_objects;
static int throw_on;
explicit Counted() {
if (throw_on == total_objects) {
TEST_THROW(1);
}
++current_objects;
++total_objects;
}
~Counted() { --current_objects; }
static void reset() {
current_objects = total_objects = 0;
throw_on = -1;
}
Counted(Counted const&) = delete;
friend void operator&(Counted) = delete;
};
int Counted::current_objects = 0;
int Counted::total_objects = 0;
int Counted::throw_on = -1;
template <typename T, int N>
struct Buffer {
alignas(T) char buffer[sizeof(T) * N] = {};
T* begin() { return reinterpret_cast<T*>(buffer); }
T* end() { return begin() + N; }
const T* cbegin() const { return reinterpret_cast<const T*>(buffer); }
const T* cend() const { return cbegin() + N; }
};
// Because this is a variable and not a function, it's guaranteed that ADL won't be used. However,
// implementations are allowed to use a different mechanism to achieve this effect, so this check is
// libc++-specific.
LIBCPP_STATIC_ASSERT(std::is_class_v<decltype(std::ranges::uninitialized_default_construct_n)>);
struct NotDefaultCtrable { NotDefaultCtrable() = delete; };
static_assert(!std::is_invocable_v<decltype(std::ranges::uninitialized_default_construct_n),
NotDefaultCtrable*, int>);
int main(int, char**) {
// An empty range -- no default constructors should be invoked.
{
Buffer<Counted, 1> buf;
std::ranges::uninitialized_default_construct_n(buf.begin(), 0);
assert(Counted::current_objects == 0);
assert(Counted::total_objects == 0);
}
// A range containing several objects.
{
constexpr int N = 5;
Buffer<Counted, N> buf;
std::ranges::uninitialized_default_construct_n(buf.begin(), N);
assert(Counted::current_objects == N);
assert(Counted::total_objects == N);
std::destroy(buf.begin(), buf.end());
assert(Counted::current_objects == 0);
assert(Counted::total_objects == N);
Counted::reset();
}
// An exception is thrown while objects are being created -- the existing objects should stay
// valid.
#ifndef TEST_HAS_NO_EXCEPTIONS
{
constexpr int N = 5;
Buffer<Counted, N> buf;
Counted::throw_on = 3; // When constructing the fourth object (counting from one).
try {
std::ranges::uninitialized_default_construct_n(buf.begin(), N);
} catch(...) {}
assert(Counted::current_objects == 0);
assert(Counted::total_objects == 3);
std::destroy(buf.begin(), buf.begin() + Counted::total_objects);
Counted::reset();
}
#endif // TEST_HAS_NO_EXCEPTIONS
// Works with const iterators.
{
constexpr int N = 5;
Buffer<Counted, N> buf;
std::ranges::uninitialized_default_construct_n(buf.cbegin(), N);
assert(Counted::current_objects == N);
assert(Counted::total_objects == N);
std::destroy(buf.begin(), buf.end());
Counted::reset();
}
return 0;
}

View File

@ -23,7 +23,6 @@
struct Counted {
static int count;
static int constructed;
static void reset() { count = constructed = 0; }
explicit Counted() { ++count; ++constructed; }
Counted(Counted const&) { assert(false); }
~Counted() { --count; }
@ -37,7 +36,6 @@ struct ThrowsCounted {
static int count;
static int constructed;
static int throw_after;
static void reset() { throw_after = count = constructed = 0; }
explicit ThrowsCounted() {
++constructed;
if (throw_after > 0 && --throw_after == 0) {
@ -67,7 +65,7 @@ void test_ctor_throws()
assert(false);
} catch (...) {}
assert(ThrowsCounted::count == 0);
assert(ThrowsCounted::constructed == 4); // forth construction throws
assert(ThrowsCounted::constructed == 4); // Fourth construction throws
#endif
}
@ -87,26 +85,9 @@ void test_counted()
assert(Counted::count == 0);
}
void test_value_initialized()
{
using It = forward_iterator<int*>;
const int N = 5;
int pool[N] = {-1, -1, -1, -1, -1};
int* p = pool;
std::uninitialized_default_construct(It(p), It(p+1));
assert(pool[0] == -1);
assert(pool[1] == -1);
std::uninitialized_default_construct(It(p+1), It(p+N));
assert(pool[1] == -1);
assert(pool[2] == -1);
assert(pool[3] == -1);
assert(pool[4] == -1);
}
int main(int, char**)
{
test_counted();
test_value_initialized();
test_ctor_throws();
return 0;

View File

@ -88,29 +88,9 @@ void test_counted()
assert(Counted::count == 0);
}
void test_value_initialized()
{
using It = forward_iterator<int*>;
const int N = 5;
int pool[N] = {-1, -1, -1, -1, -1};
int* p = pool;
auto e = std::uninitialized_default_construct_n(It(p), 1);
assert(e == It(p+1));
assert(pool[0] == -1);
assert(pool[1] == -1);
e = std::uninitialized_default_construct_n(It(p+1), 4);
assert(e == It(p+N));
assert(pool[1] == -1);
assert(pool[2] == -1);
assert(pool[3] == -1);
assert(pool[4] == -1);
}
int main(int, char**)
{
test_counted();
test_value_initialized();
test_ctor_throws();
return 0;