mirror of
https://github.com/microsoft/GSL
synced 2026-08-26 04:23:04 -04:00
feat: implementation of gsl::dyn_array (#1228)
* feat: implementation of gsl::dyn_array Implement gsl::dyn_array<T, Allocator> as specified by the CppCoreGuidlines here: https://github.com/isocpp/CppCoreGuidelines/blob/master/docs/dyn_array.md Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com>
This commit is contained in:
parent
b2f6bec48e
commit
a75212b9f3
11 changed files with 1364 additions and 24 deletions
19
.github/workflows/compilers.yml
vendored
19
.github/workflows/compilers.yml
vendored
|
|
@ -77,23 +77,30 @@ jobs:
|
|||
cmake_preset: clang-${{ matrix.cxx_version }}-${{ matrix.build_type == 'Debug' && 'debug' || 'release' }}
|
||||
extra_cmake_configure_args: '-DCMAKE_CXX_FLAGS="-isysroot \"$(xcode-select --print-path)/Platforms/MacOSX.platform/Developer/SDKs/MacOSX.sdk\""'
|
||||
|
||||
VisualStudio:
|
||||
msvc:
|
||||
strategy:
|
||||
matrix:
|
||||
generator: [ 'Visual Studio 17 2022' ]
|
||||
image: [ windows-2022, windows-2025 ]
|
||||
build_type: [ Debug, Release ]
|
||||
extra_args: [ '', '-T ClangCL' ]
|
||||
toolset: [ 'msvc', 'ClangCL' ]
|
||||
cxx_version: [ 14, 17, 20, 23 ]
|
||||
include:
|
||||
# Regular MSVC builds use Ninja (from preset)
|
||||
- toolset: 'msvc'
|
||||
generator_override: ''
|
||||
# ClangCL builds require Visual Studio generator
|
||||
- toolset: 'ClangCL'
|
||||
generator_override: '-G "Visual Studio 17 2022" -T ClangCL'
|
||||
runs-on: ${{ matrix.image }}
|
||||
steps:
|
||||
- uses: actions/checkout@v6
|
||||
- uses: microsoft/setup-msbuild@v3
|
||||
- uses: ilammy/msvc-dev-cmd@v1
|
||||
|
||||
- name: Run CMake (configure, build, test)
|
||||
uses: ./.github/workflows/cmake
|
||||
with:
|
||||
cmake_preset: msvc-${{ matrix.cxx_version }}-${{ matrix.build_type == 'Debug' && 'debug' || 'release' }}
|
||||
extra_cmake_configure_args: ${{ matrix.extra_args }}
|
||||
extra_cmake_build_args: --config ${{ matrix.build_type }}
|
||||
extra_ctest_args: -C ${{ matrix.build_type }}
|
||||
extra_cmake_configure_args: ${{ matrix.generator_override }}
|
||||
extra_cmake_build_args: ${{ matrix.toolset == 'ClangCL' && format('--config {0}', matrix.build_type) || '' }}
|
||||
extra_ctest_args: ${{ matrix.toolset == 'ClangCL' && format('-C {0}', matrix.build_type) || '' }}
|
||||
|
|
|
|||
|
|
@ -6,6 +6,7 @@
|
|||
"hidden": true,
|
||||
"binaryDir": "${sourceDir}/build/${presetName}",
|
||||
"installDir": "${sourceDir}/install/${presetName}",
|
||||
"generator": "Ninja",
|
||||
"cacheVariables": {
|
||||
"GSL_CXX_STANDARD": "14",
|
||||
"GSL_TEST": "ON"
|
||||
|
|
@ -15,14 +16,11 @@
|
|||
"name": "msvc-base",
|
||||
"inherits": "base",
|
||||
"hidden": true,
|
||||
"generator": "Visual Studio 17 2022",
|
||||
"condition": {
|
||||
"type": "equals",
|
||||
"lhs": "${hostSystemName}",
|
||||
"rhs": "Windows"
|
||||
},
|
||||
"toolset": "host=x64",
|
||||
"architecture": "x64",
|
||||
"cacheVariables": {
|
||||
"CMAKE_CXX_COMPILER": "cl"
|
||||
}
|
||||
|
|
@ -31,7 +29,6 @@
|
|||
"name": "gcc-base",
|
||||
"inherits": "base",
|
||||
"hidden": true,
|
||||
"generator": "Unix Makefiles",
|
||||
"cacheVariables": {
|
||||
"CMAKE_CXX_COMPILER": "g++",
|
||||
"CMAKE_C_COMPILER": "gcc"
|
||||
|
|
@ -41,7 +38,6 @@
|
|||
"name": "clang-base",
|
||||
"inherits": "base",
|
||||
"hidden": true,
|
||||
"generator": "Unix Makefiles",
|
||||
"cacheVariables": {
|
||||
"CMAKE_CXX_COMPILER": "clang++",
|
||||
"CMAKE_C_COMPILER": "clang"
|
||||
|
|
|
|||
|
|
@ -41,7 +41,7 @@ span_p | &
|
|||
[cu32zstring](docs/headers.md#user-content-H-zstring) | ☑ | An alias to `basic_zstring` with dynamic extent and a char type of `const char32_t`
|
||||
[**2. Owners**][cg-owners] | |
|
||||
stack_array | ☐ | A stack-allocated array
|
||||
dyn_array | ☐ | A heap-allocated array
|
||||
dyn_array | ☑ | A heap-allocated array
|
||||
[**3. Assertions**][cg-assertions] | |
|
||||
[Expects](docs/headers.md#user-content-H-assert-expects) | ☑ | A precondition assertion; on failure it terminates
|
||||
[Ensures](docs/headers.md#user-content-H-assert-ensures) | ☑ | A postcondition assertion; on failure it terminates
|
||||
|
|
|
|||
173
docs/headers.md
173
docs/headers.md
|
|
@ -9,6 +9,7 @@ See [GSL: Guidelines support library](https://isocpp.github.io/CppCoreGuidelines
|
|||
- [`<algorithms>`](#user-content-H-algorithms)
|
||||
- [`<assert>`](#user-content-H-assert)
|
||||
- [`<byte>`](#user-content-H-byte)
|
||||
- [`<dyn_array>`](#user-content-H-dyn_array)
|
||||
- [`<gsl>`](#user-content-H-gsl)
|
||||
- [`<narrow>`](#user-content-H-narrow)
|
||||
- [`<pointers>`](#user-content-H-pointers)
|
||||
|
|
@ -155,6 +156,178 @@ constexpr byte to_byte() noexcept;
|
|||
|
||||
Convert the given value `I` to a `byte`. The template requires `I` to be in the valid range 0..255 for a `gsl::byte`.
|
||||
|
||||
## <a name="H-dyn_array" />`<dyn_array>`
|
||||
|
||||
This header contains an owning dynamically allocated array type whose size is fixed between assignments.
|
||||
|
||||
- [`gsl::dyn_array`](#user-content-H-dyn_array-dyn_array)
|
||||
|
||||
### <a name="H-dyn_array-dyn_array" />`gsl::dyn_array`
|
||||
|
||||
```cpp
|
||||
template <typename T, typename Allocator = std::allocator<T>>
|
||||
class dyn_array;
|
||||
```
|
||||
|
||||
`gsl::dyn_array` owns a contiguous sequence of `T` objects allocated with `Allocator`.
|
||||
The number of elements is established when the object is constructed and remains unchanged until the object is copy-assigned.
|
||||
It provides bounds-checked element access and checked random-access iterators.
|
||||
|
||||
`gsl::dyn_array` is useful when the number of elements is known only at runtime, but the array should not grow or shrink through container operations.
|
||||
|
||||
#### Member Types
|
||||
|
||||
```cpp
|
||||
using value_type = T;
|
||||
using reference = T&;
|
||||
using const_reference = const T&;
|
||||
using iterator = details::dyn_array_iterator<T>;
|
||||
using const_iterator = details::dyn_array_iterator<const T>;
|
||||
using reverse_iterator = std::reverse_iterator<iterator>;
|
||||
using const_reverse_iterator = std::reverse_iterator<const_iterator>;
|
||||
using difference_type = std::ptrdiff_t;
|
||||
using size_type = std::size_t;
|
||||
|
||||
using allocator_type = Allocator;
|
||||
```
|
||||
|
||||
#### Member functions
|
||||
|
||||
##### Construct/Copy
|
||||
|
||||
```cpp
|
||||
explicit constexpr dyn_array(const Allocator& alloc = {});
|
||||
```
|
||||
|
||||
Constructs an empty `dyn_array`.
|
||||
No elements are allocated and `data()` returns `nullptr`.
|
||||
|
||||
```cpp
|
||||
constexpr explicit dyn_array(size_type count, const Allocator& alloc = {});
|
||||
|
||||
constexpr dyn_array(size_type count, const T& value, const Allocator& alloc = {});
|
||||
```
|
||||
|
||||
Constructs a `dyn_array` with `count` elements using `alloc`.
|
||||
The first overload default-constructs each element.
|
||||
The second overload constructs each element as a copy of `value`.
|
||||
|
||||
```cpp
|
||||
template <typename InputIt>
|
||||
constexpr dyn_array(InputIt first, InputIt last, const Allocator& alloc = {});
|
||||
```
|
||||
|
||||
Constructs a `dyn_array` by copying the elements in the range `[first, last)`.
|
||||
|
||||
```cpp
|
||||
template <std::ranges::input_range InputRg>
|
||||
constexpr dyn_array(std::from_range_t, InputRg&& rg, const Allocator& alloc = {});
|
||||
```
|
||||
|
||||
Constructs a `dyn_array` by copying the elements in `rg`.
|
||||
This overload is available when container ranges are supported.
|
||||
|
||||
```cpp
|
||||
constexpr dyn_array(const dyn_array& other, const Allocator& alloc = {});
|
||||
|
||||
constexpr dyn_array(std::initializer_list<T> init, const Allocator& alloc = {});
|
||||
```
|
||||
|
||||
Constructs a `dyn_array` by copying the elements from another `dyn_array` or from an initializer list.
|
||||
|
||||
```cpp
|
||||
constexpr auto operator=(const dyn_array& other) -> dyn_array&;
|
||||
|
||||
constexpr dyn_array(dyn_array&&) = delete;
|
||||
dyn_array& operator=(dyn_array&&) = delete;
|
||||
```
|
||||
|
||||
Copy assignment replaces the contents with copies of the elements in `other`.
|
||||
Move construction and move assignment are explicitly deleted.
|
||||
|
||||
##### Observers
|
||||
|
||||
```cpp
|
||||
constexpr auto size() const;
|
||||
constexpr auto empty() const;
|
||||
constexpr auto max_size() const;
|
||||
constexpr auto get_allocator() -> Allocator&;
|
||||
```
|
||||
|
||||
Returns the number of elements, whether the array is empty, the maximum representable size, or the allocator used by the `dyn_array`.
|
||||
|
||||
##### Element access
|
||||
|
||||
```cpp
|
||||
constexpr auto operator[](size_type pos) -> reference;
|
||||
constexpr auto operator[](size_type pos) const -> const_reference;
|
||||
```
|
||||
|
||||
Returns a reference to the element at the given index.
|
||||
[`Expects`](#user-content-H-assert-expects) that `pos` is less than the `dyn_array`'s size.
|
||||
|
||||
```cpp
|
||||
constexpr auto data();
|
||||
constexpr auto data() const -> const T*;
|
||||
```
|
||||
|
||||
Returns a pointer to the beginning of the contained data.
|
||||
If the `dyn_array` is empty, this returns `nullptr`.
|
||||
|
||||
##### Iterators
|
||||
|
||||
```cpp
|
||||
constexpr auto begin();
|
||||
constexpr auto begin() const;
|
||||
constexpr auto cbegin() const;
|
||||
|
||||
constexpr auto end();
|
||||
constexpr auto end() const;
|
||||
constexpr auto cend() const;
|
||||
```
|
||||
|
||||
Returns an iterator to the first element or to one past the last element.
|
||||
|
||||
```cpp
|
||||
constexpr auto rbegin();
|
||||
constexpr auto rbegin() const;
|
||||
constexpr auto crbegin() const;
|
||||
|
||||
constexpr auto rend();
|
||||
constexpr auto rend() const;
|
||||
constexpr auto crend() const;
|
||||
```
|
||||
|
||||
Returns a reverse iterator to the first element of the reversed range or to one past the last element of the reversed range.
|
||||
|
||||
The iterators are random-access iterators and perform bounds checking.
|
||||
Dereferencing `end()`, moving before `begin()` or past `end()`, or comparing iterators from different arrays violates preconditions.
|
||||
|
||||
##### Comparisons
|
||||
|
||||
```cpp
|
||||
constexpr auto operator==(const dyn_array& other) const;
|
||||
constexpr auto operator!=(const dyn_array& other) const;
|
||||
```
|
||||
|
||||
Compares two `dyn_array`s by size and element value.
|
||||
|
||||
#### Deduction guides
|
||||
|
||||
```cpp
|
||||
template <class InputIt,
|
||||
class Alloc = std::allocator<typename std::iterator_traits<InputIt>::value_type>>
|
||||
dyn_array(InputIt, InputIt,
|
||||
Alloc = {}) -> dyn_array<typename std::iterator_traits<InputIt>::value_type, Alloc>;
|
||||
|
||||
template <std::ranges::input_range InputRg,
|
||||
class Alloc = std::allocator<std::ranges::range_value_t<InputRg>>>
|
||||
dyn_array(std::from_range_t, InputRg&&,
|
||||
Alloc = {}) -> dyn_array<std::ranges::range_value_t<InputRg>, Alloc>;
|
||||
```
|
||||
|
||||
The range deduction guide is available when container ranges are supported.
|
||||
|
||||
## <a name="H-gsl" />`<gsl>`
|
||||
|
||||
This header is a convenience header that includes all other [GSL headers](#user-content-H).
|
||||
|
|
|
|||
439
include/gsl/dyn_array
Normal file
439
include/gsl/dyn_array
Normal file
|
|
@ -0,0 +1,439 @@
|
|||
// -*- C++ -*-
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// Copyright (c) 2026 Microsoft Corporation. All rights reserved.
|
||||
//
|
||||
// This code is licensed under the MIT License (MIT).
|
||||
//
|
||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
// THE SOFTWARE.
|
||||
//
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
#ifndef GSL_DYN_ARRAY_H
|
||||
#define GSL_DYN_ARRAY_H
|
||||
|
||||
#include "./assert"
|
||||
#include "./narrow"
|
||||
#include "./util"
|
||||
|
||||
#include <algorithm>
|
||||
#include <iterator>
|
||||
#include <memory>
|
||||
#include <type_traits>
|
||||
|
||||
#if defined(__cpp_lib_ranges) && (__cpp_lib_ranges >= 201911L)
|
||||
#include <ranges>
|
||||
#endif /* __cpp_lib_ranges >= 201911L */
|
||||
|
||||
namespace gsl
|
||||
{
|
||||
namespace details
|
||||
{
|
||||
template <typename T, typename Allocator = std::allocator<T>>
|
||||
class dyn_array_base : public Allocator
|
||||
{
|
||||
using pointer = T*;
|
||||
using size_type = std::size_t;
|
||||
|
||||
template <typename... Args>
|
||||
GSL_CONSTEXPR_SINCE_CPP20 void construct(pointer ptr, Args&&... args)
|
||||
{
|
||||
std::allocator_traits<Allocator>::construct(static_cast<Allocator&>(*this), ptr,
|
||||
std::forward<Args>(args)...);
|
||||
}
|
||||
|
||||
GSL_CONSTEXPR_SINCE_CPP20 void destroy(pointer ptr)
|
||||
{
|
||||
std::allocator_traits<Allocator>::destroy(static_cast<Allocator&>(*this), ptr);
|
||||
}
|
||||
|
||||
GSL_CONSTEXPR_SINCE_CPP20 void destroy_range(pointer first, pointer last)
|
||||
{
|
||||
for (; first != last; ++first) { destroy(first); }
|
||||
}
|
||||
|
||||
GSL_CONSTEXPR_SINCE_CPP20 void rollback_construction(pointer first, pointer last)
|
||||
{
|
||||
destroy_range(first, last);
|
||||
std::allocator_traits<Allocator>::deallocate(static_cast<Allocator&>(*this), _data,
|
||||
_count);
|
||||
_data = nullptr;
|
||||
_count = 0;
|
||||
}
|
||||
|
||||
protected:
|
||||
constexpr auto data() const { return _data; }
|
||||
|
||||
constexpr auto count() const { return _count; }
|
||||
|
||||
GSL_CONSTEXPR_SINCE_CPP20 void resize(size_type count)
|
||||
{
|
||||
// This should only be called when constructing a non-forward iterator.
|
||||
// It neither frees nor copies `_data`.
|
||||
Expects(_data == nullptr && _count == 0);
|
||||
if (count != 0)
|
||||
{
|
||||
_data = std::allocator_traits<Allocator>::allocate(static_cast<Allocator&>(*this),
|
||||
count);
|
||||
_count = count;
|
||||
}
|
||||
}
|
||||
|
||||
GSL_CONSTEXPR_SINCE_CPP20 void fill(pointer first, size_type count, const T& value)
|
||||
{
|
||||
pointer current = first;
|
||||
try
|
||||
{
|
||||
for (size_type i = 0; i < count; ++i, ++current) { construct(current, value); }
|
||||
} catch (...)
|
||||
{
|
||||
rollback_construction(first, current);
|
||||
throw;
|
||||
}
|
||||
}
|
||||
|
||||
template <typename InputIt>
|
||||
GSL_CONSTEXPR_SINCE_CPP20 void copy(InputIt first, InputIt last, pointer output)
|
||||
{
|
||||
pointer current = output;
|
||||
try
|
||||
{
|
||||
for (; first != last; ++first, ++current) { construct(current, *first); }
|
||||
} catch (...)
|
||||
{
|
||||
rollback_construction(output, current);
|
||||
throw;
|
||||
}
|
||||
}
|
||||
|
||||
GSL_CONSTEXPR_SINCE_CPP20 void default_construct(pointer first, size_type count)
|
||||
{
|
||||
pointer current = first;
|
||||
try
|
||||
{
|
||||
for (size_type i = 0; i < count; ++i, ++current) { construct(current); }
|
||||
} catch (...)
|
||||
{
|
||||
rollback_construction(first, current);
|
||||
throw;
|
||||
}
|
||||
}
|
||||
|
||||
private:
|
||||
pointer _data;
|
||||
size_type _count;
|
||||
|
||||
public:
|
||||
constexpr dyn_array_base(const Allocator& alloc)
|
||||
: Allocator{alloc}, _data{nullptr}, _count{0}
|
||||
{
|
||||
Ensures((_count == 0 && _data == nullptr) || (_count > 0 && _data != nullptr));
|
||||
}
|
||||
|
||||
constexpr dyn_array_base(size_type count, const Allocator& alloc)
|
||||
: Allocator{alloc}
|
||||
, _data{count == 0 ? nullptr
|
||||
: std::allocator_traits<Allocator>::allocate(
|
||||
static_cast<Allocator&>(*this), count)}
|
||||
, _count{count}
|
||||
{
|
||||
Ensures((_count == 0 && _data == nullptr) || (_count > 0 && _data != nullptr));
|
||||
}
|
||||
|
||||
GSL_CONSTEXPR_SINCE_CPP20 ~dyn_array_base()
|
||||
{
|
||||
if (_data)
|
||||
{
|
||||
if (!std::is_trivially_destructible<T>::value)
|
||||
{
|
||||
destroy_range(_data, _data + _count);
|
||||
}
|
||||
std::allocator_traits<Allocator>::deallocate(static_cast<Allocator&>(*this), _data,
|
||||
_count);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
template <typename T>
|
||||
class dyn_array_iterator
|
||||
{
|
||||
using size_type = std::size_t;
|
||||
|
||||
public:
|
||||
using difference_type = std::ptrdiff_t;
|
||||
using value_type = T;
|
||||
using pointer = T*;
|
||||
using reference = T&;
|
||||
using const_reference = const T&;
|
||||
using iterator_category = std::random_access_iterator_tag;
|
||||
|
||||
#if defined(__cpp_lib_ranges) && (__cpp_lib_ranges >= 201911L)
|
||||
constexpr dyn_array_iterator() = default;
|
||||
#endif /* __cpp_lib_ranges >= 201911L */
|
||||
|
||||
constexpr dyn_array_iterator(pointer ptr, size_type pos, size_type end_pos)
|
||||
: _ptr{ptr}, _pos{pos}, _end_pos{end_pos}
|
||||
{
|
||||
Ensures((_ptr != nullptr && _end_pos > 0) || (_ptr == nullptr && _end_pos == 0));
|
||||
Ensures(_pos <= _end_pos);
|
||||
}
|
||||
|
||||
#if defined(_MSC_VER) && defined(__cpp_lib_ranges) && (__cpp_lib_ranges >= 201911L)
|
||||
constexpr operator pointer() const { return _ptr + gsl::narrow<size_type>(_pos); }
|
||||
#endif /* defined(_MSC_VER) && __cpp_lib_ranges >= 201911L */
|
||||
|
||||
constexpr auto operator==(const dyn_array_iterator& other) const
|
||||
{
|
||||
Expects(_ptr == other._ptr);
|
||||
Expects(_end_pos == other._end_pos);
|
||||
return _pos == other._pos;
|
||||
}
|
||||
|
||||
constexpr auto operator!=(const dyn_array_iterator& other) const
|
||||
{
|
||||
return !(*this == other);
|
||||
}
|
||||
|
||||
constexpr auto operator*() const -> reference
|
||||
{
|
||||
Expects(_ptr != nullptr);
|
||||
Expects(_pos < _end_pos);
|
||||
return _ptr[_pos];
|
||||
}
|
||||
|
||||
constexpr auto operator++() -> dyn_array_iterator&
|
||||
{
|
||||
Expects(_pos < _end_pos);
|
||||
++_pos;
|
||||
return *this;
|
||||
}
|
||||
|
||||
constexpr auto operator++(int)
|
||||
{
|
||||
auto rv = *this;
|
||||
++(*this);
|
||||
return rv;
|
||||
}
|
||||
|
||||
constexpr auto operator--() -> dyn_array_iterator&
|
||||
{
|
||||
Expects(_pos > 0);
|
||||
--_pos;
|
||||
return *this;
|
||||
}
|
||||
|
||||
constexpr auto operator--(int)
|
||||
{
|
||||
auto rv = *this;
|
||||
--(*this);
|
||||
return rv;
|
||||
}
|
||||
|
||||
constexpr auto operator+=(difference_type diff) -> dyn_array_iterator&
|
||||
{
|
||||
auto new_pos = gsl::narrow<difference_type>(_pos) + diff;
|
||||
Expects(new_pos >= 0);
|
||||
Expects(new_pos <= gsl::narrow<difference_type>(_end_pos));
|
||||
_pos = gsl::narrow<size_type>(new_pos);
|
||||
return *this;
|
||||
}
|
||||
|
||||
constexpr auto operator-=(difference_type diff) -> dyn_array_iterator&
|
||||
{
|
||||
auto new_pos = gsl::narrow<difference_type>(_pos) - diff;
|
||||
Expects(new_pos >= 0);
|
||||
Expects(new_pos <= gsl::narrow<difference_type>(_end_pos));
|
||||
_pos = gsl::narrow<size_type>(new_pos);
|
||||
return *this;
|
||||
}
|
||||
|
||||
constexpr auto operator+(difference_type diff) const
|
||||
{
|
||||
auto new_pos = gsl::narrow<difference_type>(_pos) + diff;
|
||||
return dyn_array_iterator{_ptr, gsl::narrow<size_type>(new_pos), _end_pos};
|
||||
}
|
||||
|
||||
constexpr auto operator-(difference_type diff) const { return *this + (-diff); }
|
||||
|
||||
constexpr auto operator-(const dyn_array_iterator& other) const
|
||||
{
|
||||
Expects(_ptr == other._ptr);
|
||||
Expects(_end_pos == other._end_pos);
|
||||
return gsl::narrow<difference_type>(_pos) - gsl::narrow<difference_type>(other._pos);
|
||||
}
|
||||
|
||||
constexpr auto operator[](size_type pos) -> reference
|
||||
{
|
||||
Expects(_pos + pos < _end_pos);
|
||||
return _ptr[_pos + pos];
|
||||
}
|
||||
|
||||
constexpr auto operator[](size_type pos) const -> const_reference
|
||||
{
|
||||
return const_cast<dyn_array_iterator&>(*this).operator[](pos);
|
||||
}
|
||||
|
||||
private:
|
||||
pointer _ptr{};
|
||||
size_type _pos{};
|
||||
size_type _end_pos{};
|
||||
};
|
||||
} // namespace details
|
||||
|
||||
template <typename T, typename Allocator = std::allocator<T>>
|
||||
class dyn_array : private details::dyn_array_base<T, Allocator>
|
||||
{
|
||||
using base = details::dyn_array_base<T, Allocator>;
|
||||
using pointer = T*;
|
||||
|
||||
public:
|
||||
using value_type = T;
|
||||
using reference = T&;
|
||||
using const_reference = const T&;
|
||||
using iterator = details::dyn_array_iterator<T>;
|
||||
using const_iterator = details::dyn_array_iterator<const T>;
|
||||
using reverse_iterator = std::reverse_iterator<iterator>;
|
||||
using const_reverse_iterator = std::reverse_iterator<const_iterator>;
|
||||
using difference_type = std::ptrdiff_t;
|
||||
using size_type = std::size_t;
|
||||
|
||||
using allocator_type = Allocator;
|
||||
|
||||
explicit constexpr dyn_array(const Allocator& alloc = {}) : base{alloc} {}
|
||||
|
||||
constexpr dyn_array(size_type count, const T& value, const Allocator& alloc = {})
|
||||
: base{count, alloc}
|
||||
{
|
||||
base::fill(data(), size(), value);
|
||||
}
|
||||
|
||||
template <typename InputIt,
|
||||
std::enable_if_t<details::is_fwd_iterator<InputIt>::value, bool> = true>
|
||||
constexpr dyn_array(InputIt first, InputIt last, const Allocator& alloc = {})
|
||||
: base{gsl::narrow<size_type>(std::distance(first, last)), alloc}
|
||||
{
|
||||
base::copy(first, last, data());
|
||||
}
|
||||
|
||||
template <typename InputIt, std::enable_if_t<!details::is_fwd_iterator<InputIt>::value &&
|
||||
details::is_iterator<InputIt>::value,
|
||||
bool> = true>
|
||||
constexpr dyn_array(InputIt first, InputIt last, const Allocator& alloc = {}) : dyn_array{alloc}
|
||||
{
|
||||
std::vector<T> tmp(first, last);
|
||||
base::resize(tmp.size());
|
||||
base::copy(std::begin(tmp), std::end(tmp), data());
|
||||
}
|
||||
|
||||
#if defined(__cpp_lib_containers_ranges) && (__cpp_lib_containers_ranges >= 202202L)
|
||||
template <typename InputRg>
|
||||
requires(std::ranges::input_range<InputRg>)
|
||||
constexpr dyn_array(std::from_range_t, InputRg&& rg, const Allocator& alloc = {})
|
||||
: base{gsl::narrow<size_type>(std::size(rg)), alloc}
|
||||
{
|
||||
base::copy(std::ranges::begin(rg), std::ranges::end(rg), data());
|
||||
}
|
||||
#endif /* __cpp_lib_containers_ranges >= 202202L */
|
||||
|
||||
constexpr explicit dyn_array(size_type count, const Allocator& alloc = {}) : base{count, alloc}
|
||||
{
|
||||
base::default_construct(data(), size());
|
||||
}
|
||||
|
||||
constexpr dyn_array(const dyn_array& other, const Allocator& alloc = {})
|
||||
: dyn_array(other.begin(), other.end(), alloc)
|
||||
{}
|
||||
|
||||
constexpr dyn_array(std::initializer_list<T> init, const Allocator& alloc = {})
|
||||
: dyn_array(init.begin(), init.end(), alloc)
|
||||
{}
|
||||
|
||||
constexpr dyn_array(dyn_array&&) = delete;
|
||||
dyn_array& operator=(dyn_array&&) = delete;
|
||||
|
||||
constexpr auto operator==(const dyn_array& other) const
|
||||
{
|
||||
return size() == other.size() && std::equal(begin(), end(), other.begin(), other.end());
|
||||
}
|
||||
|
||||
constexpr auto operator!=(const dyn_array& other) const { return !(*this == other); }
|
||||
|
||||
constexpr auto size() const { return base::count(); }
|
||||
|
||||
constexpr auto empty() const { return size() == 0; }
|
||||
|
||||
constexpr auto max_size() const { return static_cast<size_type>(-1); }
|
||||
|
||||
constexpr auto get_allocator() -> Allocator& { return *this; }
|
||||
|
||||
constexpr auto operator[](size_type pos) -> reference
|
||||
{
|
||||
Expects(pos < size());
|
||||
return data()[pos];
|
||||
}
|
||||
|
||||
constexpr auto operator[](size_type pos) const -> const_reference
|
||||
{
|
||||
return const_cast<dyn_array&>(*this)[pos];
|
||||
}
|
||||
|
||||
constexpr auto data() { return base::data(); }
|
||||
constexpr auto data() const -> const T* { return const_cast<dyn_array&>(*this).data(); }
|
||||
|
||||
constexpr auto begin() { return iterator{data(), 0, size()}; }
|
||||
constexpr auto begin() const { return const_iterator{data(), 0, size()}; }
|
||||
constexpr auto cbegin() const { return begin(); }
|
||||
|
||||
constexpr auto rbegin() { return reverse_iterator{end()}; }
|
||||
constexpr auto rbegin() const { return const_reverse_iterator{end()}; }
|
||||
constexpr auto crbegin() const { return rbegin(); }
|
||||
|
||||
#ifdef _MSC_VER
|
||||
constexpr auto _Unchecked_begin() { return data(); }
|
||||
constexpr auto _Unchecked_begin() const -> const T*
|
||||
{
|
||||
return const_cast<dyn_array&>(*this)._Unchecked_begin();
|
||||
}
|
||||
#endif /* _MSC_VER */
|
||||
|
||||
constexpr auto end() { return iterator{data(), size(), size()}; }
|
||||
constexpr auto end() const { return const_iterator{data(), size(), size()}; }
|
||||
constexpr auto cend() const { return end(); }
|
||||
|
||||
constexpr auto rend() { return reverse_iterator{begin()}; }
|
||||
constexpr auto rend() const { return const_reverse_iterator{begin()}; }
|
||||
constexpr auto crend() const { return rend(); }
|
||||
|
||||
#ifdef _MSC_VER
|
||||
constexpr auto _Unchecked_end() { return data() + size(); }
|
||||
constexpr auto _Unchecked_end() const -> const T*
|
||||
{
|
||||
return const_cast<dyn_array&>(*this)._Unchecked_end();
|
||||
}
|
||||
#endif /* _MSC_VER */
|
||||
};
|
||||
|
||||
#if defined(__cpp_deduction_guides) && (__cpp_deduction_guides >= 201703L)
|
||||
|
||||
template <class InputIt,
|
||||
class Alloc = std::allocator<typename std::iterator_traits<InputIt>::value_type>>
|
||||
dyn_array(InputIt, InputIt, Alloc = {})
|
||||
-> dyn_array<typename std::iterator_traits<InputIt>::value_type, Alloc>;
|
||||
|
||||
#if defined(__cpp_lib_containers_ranges) && (__cpp_lib_containers_ranges >= 202202L)
|
||||
template <std::ranges::input_range InputRg,
|
||||
class Alloc = std::allocator<std::ranges::range_value_t<InputRg>>>
|
||||
dyn_array(std::from_range_t, InputRg&&, Alloc = {})
|
||||
-> dyn_array<std::ranges::range_value_t<InputRg>, Alloc>;
|
||||
#endif /* __cpp_lib_containers_ranges >= 202202L */
|
||||
|
||||
#endif /* __cpp_deduction_guides >= 201703L */
|
||||
} // namespace gsl
|
||||
|
||||
#endif /* defined(GSL_DYN_ARRAY_H) */
|
||||
|
|
@ -1,3 +1,4 @@
|
|||
// -*- C++ -*-
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// Copyright (c) 2015 Microsoft Corporation. All rights reserved.
|
||||
|
|
@ -357,7 +358,7 @@ auto make_strict_not_null(T&& t) noexcept
|
|||
return strict_not_null<std::remove_cv_t<std::remove_reference_t<T>>>{std::forward<T>(t)};
|
||||
}
|
||||
|
||||
#if (defined(__cpp_deduction_guides) && (__cpp_deduction_guides >= 201611L))
|
||||
#if defined(__cpp_deduction_guides) && (__cpp_deduction_guides >= 201703L)
|
||||
|
||||
// deduction guides to prevent the ctad-maybe-unsupported warning
|
||||
template <class T>
|
||||
|
|
@ -365,7 +366,7 @@ not_null(T) -> not_null<T>;
|
|||
template <class T>
|
||||
strict_not_null(T) -> strict_not_null<T>;
|
||||
|
||||
#endif // ( defined(__cpp_deduction_guides) && (__cpp_deduction_guides >= 201611L) )
|
||||
#endif // defined(__cpp_deduction_guides) && (__cpp_deduction_guides >= 201703L)
|
||||
|
||||
} // namespace gsl
|
||||
|
||||
|
|
|
|||
|
|
@ -1,3 +1,4 @@
|
|||
// -*- C++ -*-
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// Copyright (c) 2015 Microsoft Corporation. All rights reserved.
|
||||
|
|
@ -747,7 +748,7 @@ private:
|
|||
}
|
||||
};
|
||||
|
||||
#if (defined(__cpp_deduction_guides) && (__cpp_deduction_guides >= 201611L))
|
||||
#if defined(__cpp_deduction_guides) && (__cpp_deduction_guides >= 201703L)
|
||||
|
||||
// Deduction Guides
|
||||
template <class Type, std::size_t Extent>
|
||||
|
|
@ -767,7 +768,7 @@ template <class Container,
|
|||
class Element = std::remove_pointer_t<decltype(std::declval<const Container&>().data())>>
|
||||
span(const Container&) -> span<Element>;
|
||||
|
||||
#endif // ( defined(__cpp_deduction_guides) && (__cpp_deduction_guides >= 201611L) )
|
||||
#endif // defined(__cpp_deduction_guides) && (__cpp_deduction_guides >= 201703L)
|
||||
|
||||
#if defined(GSL_USE_STATIC_CONSTEXPR_WORKAROUND)
|
||||
#if defined(__clang__) && defined(_MSC_VER) && defined(__cplusplus) && (__cplusplus < 201703L)
|
||||
|
|
|
|||
|
|
@ -1,3 +1,4 @@
|
|||
// -*- C++ -*-
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// Copyright (c) 2015 Microsoft Corporation. All rights reserved.
|
||||
|
|
@ -19,9 +20,9 @@
|
|||
|
||||
#include "./assert" // for Expects
|
||||
|
||||
#include <array>
|
||||
#include <cstddef> // for ptrdiff_t, size_t
|
||||
#include <initializer_list> // for initializer_list
|
||||
#include <iterator> // for iterator_traits
|
||||
#include <limits> // for numeric_limits
|
||||
#include <type_traits> // for is_signed, integral_constant
|
||||
#include <utility> // for exchange, forward
|
||||
|
|
@ -86,6 +87,13 @@
|
|||
#define GSL_DEPRECATED(msg)
|
||||
#endif // !defined(GSL_DEPRECATED)
|
||||
|
||||
#if __cplusplus >= 202002L
|
||||
#define GSL_CONSTEXPR_SINCE_CPP20 constexpr
|
||||
#else // ^^^ since C++20 /// before C++20 vvv
|
||||
#define GSL_BEFORE_CPP20
|
||||
#define GSL_CONSTEXPR_SINCE_CPP20
|
||||
#endif // __cplusplus >= 202002L
|
||||
|
||||
namespace gsl
|
||||
{
|
||||
//
|
||||
|
|
@ -95,6 +103,35 @@ namespace gsl
|
|||
// index type for all container indexes/subscripts/sizes
|
||||
using index = std::ptrdiff_t;
|
||||
|
||||
namespace details
|
||||
{
|
||||
template <typename...>
|
||||
using void_t = void;
|
||||
|
||||
template <typename T, typename = void>
|
||||
struct is_iterator : std::false_type
|
||||
{
|
||||
};
|
||||
|
||||
template <typename T>
|
||||
struct is_iterator<T, void_t<typename std::iterator_traits<T>::value_type>> : std::true_type
|
||||
{
|
||||
};
|
||||
|
||||
template <typename T, typename = void>
|
||||
struct is_fwd_iterator : std::false_type
|
||||
{
|
||||
};
|
||||
|
||||
template <typename T>
|
||||
struct is_fwd_iterator<T, void_t<typename std::iterator_traits<T>::iterator_category>>
|
||||
: std::integral_constant<
|
||||
bool, std::is_base_of<std::forward_iterator_tag,
|
||||
typename std::iterator_traits<T>::iterator_category>::value>
|
||||
{
|
||||
};
|
||||
} // namespace details
|
||||
|
||||
// final_action allows you to ensure something gets run at the end of a scope
|
||||
template <class F>
|
||||
class final_action
|
||||
|
|
|
|||
|
|
@ -151,6 +151,7 @@ else()
|
|||
-Wno-unknown-attributes
|
||||
-Wno-used-but-marked-unused # GTest EXPECT_DEATH
|
||||
-Wno-weak-vtables
|
||||
-Wno-poison-system-directories
|
||||
$<$<EQUAL:${GSL_CXX_STANDARD},14>: # no support for [[maybe_unused]]
|
||||
-Wno-unused-member-function
|
||||
-Wno-unused-variable
|
||||
|
|
@ -280,6 +281,7 @@ else()
|
|||
-Wno-missing-prototypes
|
||||
-Wno-unknown-attributes
|
||||
-Wno-weak-vtables
|
||||
-Wno-poison-system-directories
|
||||
>
|
||||
$<$<CXX_COMPILER_ID:GNU>:
|
||||
-Wdouble-promotion # float implicit to double
|
||||
|
|
|
|||
684
tests/dyn_array_tests.cpp
Normal file
684
tests/dyn_array_tests.cpp
Normal file
|
|
@ -0,0 +1,684 @@
|
|||
#include <gtest/gtest.h>
|
||||
|
||||
#include "deathTestCommon.h"
|
||||
#include "gsl/dyn_array"
|
||||
#include <cstdlib>
|
||||
#include <exception>
|
||||
#include <gsl/dyn_array>
|
||||
#include <gsl/util>
|
||||
#include <iostream>
|
||||
#include <sstream>
|
||||
#include <type_traits>
|
||||
|
||||
// Despite using <algorithm> and <ranges> utilities in this test, they
|
||||
// are not being included directly by this file as a test to ensure
|
||||
// transitive inclusion via <gsl/dyn_array>.
|
||||
|
||||
static_assert(sizeof(gsl::dyn_array<int>) == 2 * sizeof(void*),
|
||||
"gsl::dyn_array (with the default allocator) should be 16 bytes");
|
||||
|
||||
#if defined(__cpp_lib_concepts) && (__cpp_lib_concepts >= 202002L)
|
||||
static_assert(std::input_iterator<gsl::dyn_array<int>::iterator>,
|
||||
"gsl::dyn_array should expose a valid input_iterator");
|
||||
#endif /* __cpp_lib_concepts >= 202002L */
|
||||
|
||||
#if defined(__cpp_lib_ranges) && (__cpp_lib_ranges >= 201911L)
|
||||
static_assert(std::ranges::input_range<gsl::dyn_array<int>>,
|
||||
"gsl::dyn_array should be a valid input range");
|
||||
#endif /* __cpp_lib_ranges >= 201911L */
|
||||
|
||||
TEST(dyn_array_tests, default_ctor)
|
||||
{
|
||||
gsl::dyn_array<char> diamondbacks;
|
||||
EXPECT_TRUE(diamondbacks.empty());
|
||||
EXPECT_EQ(diamondbacks.size(), 0);
|
||||
EXPECT_EQ(diamondbacks.data(), nullptr);
|
||||
}
|
||||
|
||||
TEST(dyn_array_tests, count_ctor)
|
||||
{
|
||||
gsl::dyn_array<char> athletics(10);
|
||||
EXPECT_FALSE(athletics.empty());
|
||||
EXPECT_EQ(athletics.size(), 10);
|
||||
EXPECT_NE(athletics.data(), nullptr);
|
||||
|
||||
gsl::dyn_array<char> braves(0);
|
||||
EXPECT_TRUE(braves.empty());
|
||||
EXPECT_EQ(braves.size(), 0);
|
||||
EXPECT_EQ(braves.data(), nullptr);
|
||||
EXPECT_TRUE(std::all_of(braves.begin(), braves.end(), [](char c) { return c == char{}; }));
|
||||
}
|
||||
|
||||
TEST(dyn_array_tests, count_value_ctor)
|
||||
{
|
||||
gsl::dyn_array<char> orioles(10, 'c');
|
||||
EXPECT_FALSE(orioles.empty());
|
||||
EXPECT_EQ(orioles.size(), 10);
|
||||
EXPECT_NE(orioles.data(), nullptr);
|
||||
EXPECT_TRUE(std::all_of(orioles.begin(), orioles.end(), [](char c) { return c == 'c'; }));
|
||||
|
||||
gsl::dyn_array<int> redsox(10, 42);
|
||||
EXPECT_FALSE(redsox.empty());
|
||||
EXPECT_EQ(redsox.size(), 10);
|
||||
EXPECT_NE(redsox.data(), nullptr);
|
||||
EXPECT_TRUE(std::all_of(redsox.begin(), redsox.end(), [](int i) { return i == 42; }));
|
||||
}
|
||||
|
||||
TEST(dyn_array_tests, inputit_ctor)
|
||||
{
|
||||
std::vector<char> cubs(10, 'c');
|
||||
gsl::dyn_array<char> whitesox(cubs.begin(), cubs.end());
|
||||
EXPECT_FALSE(whitesox.empty());
|
||||
EXPECT_EQ(whitesox.size(), cubs.size());
|
||||
EXPECT_NE(whitesox.data(), nullptr);
|
||||
EXPECT_TRUE(std::all_of(whitesox.begin(), whitesox.end(), [](char c) { return c == 'c'; }));
|
||||
}
|
||||
|
||||
TEST(dyn_array_tests, copy_ctor)
|
||||
{
|
||||
gsl::dyn_array<char> reds(10, 'c');
|
||||
gsl::dyn_array<char> guardians(reds);
|
||||
EXPECT_FALSE(guardians.empty());
|
||||
EXPECT_EQ(guardians.size(), reds.size());
|
||||
EXPECT_NE(guardians.data(), nullptr);
|
||||
EXPECT_TRUE(std::all_of(guardians.begin(), guardians.end(), [](char c) { return c == 'c'; }));
|
||||
}
|
||||
|
||||
TEST(dyn_array_tests, access_operator)
|
||||
{
|
||||
gsl::dyn_array<char> rockies(10, 'c');
|
||||
using ST = typename decltype(rockies)::size_type;
|
||||
for (int i = 0; i < gsl::narrow<int>(rockies.size()); i++)
|
||||
EXPECT_EQ(rockies[gsl::narrow<ST>(i)], 'c');
|
||||
for (int i = 0; i < gsl::narrow<int>(rockies.size()); i++) rockies[gsl::narrow<ST>(i)] = 'r';
|
||||
for (int i = 0; i < gsl::narrow<int>(rockies.size()); i++)
|
||||
EXPECT_EQ(rockies[gsl::narrow<ST>(i)], 'r');
|
||||
gsl::dyn_array<int> tigers(10);
|
||||
for (int i = 0; i < gsl::narrow<int>(tigers.size()); i++) tigers[gsl::narrow<ST>(i)] = i;
|
||||
for (int i = 0; i < gsl::narrow<int>(tigers.size()); i++)
|
||||
EXPECT_EQ(tigers[gsl::narrow<ST>(i)], i);
|
||||
}
|
||||
|
||||
TEST(dyn_array_tests, iterators)
|
||||
{
|
||||
gsl::dyn_array<char> astros(10, 'c');
|
||||
for (auto it = astros.begin(); it != astros.end(); it++) EXPECT_EQ(*it, 'c');
|
||||
for (auto it = astros.begin(); it != astros.end(); it++) *it = 'r';
|
||||
for (auto it = astros.begin(); it != astros.end(); it++) EXPECT_EQ(*it, 'r');
|
||||
EXPECT_TRUE(std::all_of(astros.begin(), astros.end(), [](char c) { return c == 'r'; }));
|
||||
|
||||
gsl::dyn_array<char> royals(10, 'c');
|
||||
for (auto it = royals.begin(); it != royals.end(); ++it) EXPECT_EQ(*it, 'c');
|
||||
for (auto it = royals.begin(); it != royals.end(); ++it) *it = 'r';
|
||||
for (auto it = royals.begin(); it != royals.end(); ++it) EXPECT_EQ(*it, 'r');
|
||||
EXPECT_TRUE(std::all_of(royals.begin(), royals.end(), [](char c) { return c == 'r'; }));
|
||||
}
|
||||
|
||||
TEST(dyn_array_tests, range_for)
|
||||
{
|
||||
gsl::dyn_array<char> angels(10, 'c');
|
||||
for (auto x : angels) EXPECT_EQ(x, 'c');
|
||||
for (auto& x : angels) x = 'r';
|
||||
for (auto x : angels) EXPECT_EQ(x, 'r');
|
||||
EXPECT_TRUE(std::all_of(angels.begin(), angels.end(), [](char c) { return c == 'r'; }));
|
||||
}
|
||||
|
||||
TEST(dyn_array_tests, use_std_algorithms)
|
||||
{
|
||||
gsl::dyn_array<char> dodgers(26);
|
||||
std::generate(dodgers.begin(), dodgers.end(), [i = 0]() mutable { return 'a' + i++; });
|
||||
char ch = 'a';
|
||||
for (auto x : dodgers) EXPECT_EQ(x, ch++);
|
||||
EXPECT_EQ(std::find(dodgers.begin(), dodgers.end(), 'a'), dodgers.begin());
|
||||
{
|
||||
auto it = std::find(dodgers.begin(), dodgers.end(), 'c');
|
||||
EXPECT_EQ(std::distance(dodgers.begin(), it), 'c' - 'a');
|
||||
EXPECT_EQ(std::distance(it, dodgers.begin()), 'a' - 'c');
|
||||
}
|
||||
{
|
||||
auto it = std::lower_bound(dodgers.begin(), dodgers.end(), 'j');
|
||||
EXPECT_EQ(*it, 'j');
|
||||
EXPECT_EQ(std::distance(dodgers.begin(), it), 'j' - 'a');
|
||||
EXPECT_EQ(std::distance(it, dodgers.begin()), 'a' - 'j');
|
||||
}
|
||||
EXPECT_EQ(dodgers.begin(), std::begin(dodgers));
|
||||
EXPECT_EQ(dodgers.end(), std::end(dodgers));
|
||||
}
|
||||
|
||||
#if defined(__cpp_lib_constexpr_dynamic_alloc) && (__cpp_lib_constexpr_dynamic_alloc >= 201907L)
|
||||
constexpr auto default_constructed_count_dyn_array_is_constexpr()
|
||||
{
|
||||
gsl::dyn_array<int> values(3);
|
||||
return values.size() == 3 && values[0] == 0 && values[1] == 0 && values[2] == 0;
|
||||
}
|
||||
|
||||
TEST(dyn_array_tests, constexprness)
|
||||
{
|
||||
constexpr gsl::dyn_array<char> marlins;
|
||||
static_assert(marlins == marlins);
|
||||
static_assert(marlins.empty());
|
||||
static_assert(marlins.size() == 0);
|
||||
static_assert(marlins.data() == nullptr);
|
||||
static_assert(marlins.begin() == marlins.end());
|
||||
static_assert(std::distance(marlins.begin(), marlins.end()) == 0);
|
||||
static_assert(default_constructed_count_dyn_array_is_constexpr());
|
||||
}
|
||||
#endif /* __cpp_lib_constexpr_dynamic_alloc >= 201907L */
|
||||
|
||||
#if defined(__cpp_lib_ranges) && (__cpp_lib_ranges >= 201911L)
|
||||
TEST(dyn_array_tests, ranges)
|
||||
{
|
||||
gsl::dyn_array<char> brewers(26);
|
||||
std::ranges::generate(brewers, [c = 'a']() mutable { return c++; });
|
||||
char ch = 'a';
|
||||
for (auto x : brewers) EXPECT_EQ(x, ch++);
|
||||
EXPECT_EQ(std::ranges::find(brewers, 'a'), std::ranges::begin(brewers));
|
||||
{
|
||||
auto it = std::ranges::find(brewers, 'c');
|
||||
EXPECT_EQ(std::ranges::distance(std::ranges::begin(brewers), it), 'c' - 'a');
|
||||
EXPECT_EQ(std::ranges::distance(it, std::ranges::begin(brewers)), 'a' - 'c');
|
||||
}
|
||||
|
||||
#if defined(__cpp_lib_containers_ranges) && (__cpp_lib_containers_ranges >= 202202L)
|
||||
std::vector<char> twins(10, 'c');
|
||||
gsl::dyn_array<char> mets(std::from_range, twins);
|
||||
EXPECT_EQ(twins.size(), mets.size());
|
||||
EXPECT_TRUE(std::ranges::all_of(mets, [](char c) { return c == 'c'; }));
|
||||
#endif /* __cpp_lib_containers_ranges >= 202202L */
|
||||
}
|
||||
#endif /* __cpp_lib_ranges >= 201911L */
|
||||
|
||||
#if defined(__cpp_lib_constexpr_dynamic_alloc) && (__cpp_lib_constexpr_dynamic_alloc >= 201907L)
|
||||
template <typename T, unsigned N>
|
||||
struct ConstexprAllocator
|
||||
{
|
||||
using value_type = T;
|
||||
|
||||
T buf[N]{};
|
||||
std::size_t sz{};
|
||||
|
||||
constexpr ConstexprAllocator() = default;
|
||||
|
||||
template <typename U>
|
||||
constexpr ConstexprAllocator(const ConstexprAllocator<U, N>&) noexcept : buf{}, sz{}
|
||||
{}
|
||||
|
||||
template <typename U>
|
||||
struct rebind
|
||||
{
|
||||
using other = ConstexprAllocator<U, N>;
|
||||
};
|
||||
|
||||
constexpr auto allocate(std::size_t n) -> value_type*
|
||||
{
|
||||
auto addr = &buf[sz];
|
||||
sz += n;
|
||||
return addr;
|
||||
}
|
||||
|
||||
constexpr void deallocate(value_type*, std::size_t) noexcept {}
|
||||
};
|
||||
|
||||
template <typename T1, unsigned N1, typename T2, unsigned N2>
|
||||
constexpr auto operator==(const ConstexprAllocator<T1, N1>& lhs,
|
||||
const ConstexprAllocator<T2, N2>& rhs) noexcept
|
||||
{
|
||||
return std::addressof(lhs) == std::addressof(rhs);
|
||||
}
|
||||
|
||||
template <typename T1, unsigned N1, typename T2, unsigned N2>
|
||||
constexpr auto operator!=(const ConstexprAllocator<T1, N1>& lhs,
|
||||
const ConstexprAllocator<T2, N2>& rhs) noexcept
|
||||
{
|
||||
return !(lhs == rhs);
|
||||
}
|
||||
#endif /* __cpp_lib_constexpr_dynamic_alloc >= 201907L */
|
||||
|
||||
template <typename T>
|
||||
static int AllocCounter = 0;
|
||||
|
||||
template <typename T>
|
||||
static int DeallocCounter = 0;
|
||||
|
||||
struct LifetimeCounter
|
||||
{
|
||||
static int alive_count;
|
||||
|
||||
int value{};
|
||||
|
||||
explicit LifetimeCounter(int v = 0) : value(v) { ++alive_count; }
|
||||
|
||||
LifetimeCounter(const LifetimeCounter& other) : value(other.value) { ++alive_count; }
|
||||
|
||||
~LifetimeCounter() { --alive_count; }
|
||||
};
|
||||
|
||||
int LifetimeCounter::alive_count = 0;
|
||||
|
||||
struct DefaultConstructionCounter
|
||||
{
|
||||
static int default_constructor_count;
|
||||
static int copy_constructor_count;
|
||||
|
||||
int value{};
|
||||
|
||||
DefaultConstructionCounter() { ++default_constructor_count; }
|
||||
|
||||
DefaultConstructionCounter(const DefaultConstructionCounter& other) : value(other.value)
|
||||
{
|
||||
++copy_constructor_count;
|
||||
}
|
||||
|
||||
static void reset()
|
||||
{
|
||||
default_constructor_count = 0;
|
||||
copy_constructor_count = 0;
|
||||
}
|
||||
};
|
||||
|
||||
int DefaultConstructionCounter::default_constructor_count = 0;
|
||||
int DefaultConstructionCounter::copy_constructor_count = 0;
|
||||
|
||||
struct DefaultOnlyElement
|
||||
{
|
||||
DefaultOnlyElement() = default;
|
||||
DefaultOnlyElement(const DefaultOnlyElement&) = delete;
|
||||
DefaultOnlyElement& operator=(const DefaultOnlyElement&) = delete;
|
||||
DefaultOnlyElement(DefaultOnlyElement&&) = delete;
|
||||
DefaultOnlyElement& operator=(DefaultOnlyElement&&) = delete;
|
||||
};
|
||||
|
||||
struct ThrowOnCopy
|
||||
{
|
||||
static int alive_count;
|
||||
static int copy_count;
|
||||
static int throw_on_copy_index;
|
||||
|
||||
int value{};
|
||||
|
||||
explicit ThrowOnCopy(int v = 0) : value(v) { ++alive_count; }
|
||||
|
||||
ThrowOnCopy(const ThrowOnCopy& other) : value(other.value)
|
||||
{
|
||||
if (copy_count == throw_on_copy_index)
|
||||
{
|
||||
++copy_count;
|
||||
throw 42;
|
||||
}
|
||||
++copy_count;
|
||||
++alive_count;
|
||||
}
|
||||
|
||||
~ThrowOnCopy() { --alive_count; }
|
||||
};
|
||||
|
||||
int ThrowOnCopy::alive_count = 0;
|
||||
int ThrowOnCopy::copy_count = 0;
|
||||
int ThrowOnCopy::throw_on_copy_index = -1;
|
||||
|
||||
template <typename T>
|
||||
class Newocator
|
||||
{
|
||||
public:
|
||||
using value_type = T;
|
||||
|
||||
Newocator() = default;
|
||||
|
||||
template <typename U>
|
||||
Newocator(const Newocator<U>&) noexcept
|
||||
{}
|
||||
|
||||
static void init()
|
||||
{
|
||||
AllocCounter<Newocator<T>> = 0;
|
||||
DeallocCounter<Newocator<T>> = 0;
|
||||
}
|
||||
|
||||
static void check() { EXPECT_EQ(AllocCounter<Newocator<T>>, DeallocCounter<Newocator<T>>); }
|
||||
|
||||
auto allocate(std::size_t n) -> value_type*
|
||||
{
|
||||
AllocCounter<Newocator<T>> ++;
|
||||
return static_cast<value_type*>(::operator new(n * sizeof(value_type)));
|
||||
}
|
||||
|
||||
void deallocate(value_type* p, std::size_t) noexcept
|
||||
{
|
||||
DeallocCounter<Newocator<T>> ++;
|
||||
::operator delete(p);
|
||||
}
|
||||
|
||||
template <typename U>
|
||||
struct rebind
|
||||
{
|
||||
using other = Newocator<U>;
|
||||
};
|
||||
};
|
||||
|
||||
template <typename T, typename U>
|
||||
constexpr auto operator==(const Newocator<T>&, const Newocator<U>&) noexcept
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
template <typename T, typename U>
|
||||
constexpr auto operator!=(const Newocator<T>& lhs, const Newocator<U>& rhs) noexcept
|
||||
{
|
||||
return !(lhs == rhs);
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
class OwnershipTrackingAllocator
|
||||
{
|
||||
public:
|
||||
using value_type = T;
|
||||
|
||||
OwnershipTrackingAllocator() noexcept : owner_id(next_owner_id()) { ++next_owner_id(); }
|
||||
|
||||
explicit OwnershipTrackingAllocator(int owner) noexcept : owner_id(owner) {}
|
||||
|
||||
template <typename U>
|
||||
OwnershipTrackingAllocator(const OwnershipTrackingAllocator<U>& other) noexcept
|
||||
: owner_id(other.owner())
|
||||
{}
|
||||
|
||||
auto allocate(std::size_t count) -> value_type*
|
||||
{
|
||||
static_assert(alignof(value_type) <= alignof(int),
|
||||
"test allocator only supports types with int-or-smaller alignment");
|
||||
auto raw =
|
||||
static_cast<unsigned char*>(::operator new(sizeof(int) + count * sizeof(value_type)));
|
||||
*reinterpret_cast<int*>(raw) = owner_id;
|
||||
++allocation_count();
|
||||
return reinterpret_cast<value_type*>(raw + sizeof(int));
|
||||
}
|
||||
|
||||
void deallocate(value_type* pointer, std::size_t) noexcept
|
||||
{
|
||||
auto raw = reinterpret_cast<unsigned char*>(pointer) - sizeof(int);
|
||||
if (*reinterpret_cast<int*>(raw) != owner_id) { ++mismatched_deallocation_count(); }
|
||||
++deallocation_count();
|
||||
::operator delete(raw);
|
||||
}
|
||||
|
||||
auto owner() const noexcept { return owner_id; }
|
||||
|
||||
static void reset()
|
||||
{
|
||||
next_owner_id() = 1;
|
||||
allocation_count() = 0;
|
||||
deallocation_count() = 0;
|
||||
mismatched_deallocation_count() = 0;
|
||||
}
|
||||
|
||||
static auto mismatched_deallocations() { return mismatched_deallocation_count(); }
|
||||
|
||||
private:
|
||||
int owner_id;
|
||||
|
||||
static auto next_owner_id() -> int&
|
||||
{
|
||||
static int value = 1;
|
||||
return value;
|
||||
}
|
||||
|
||||
static auto allocation_count() -> int&
|
||||
{
|
||||
static int value = 0;
|
||||
return value;
|
||||
}
|
||||
|
||||
static auto deallocation_count() -> int&
|
||||
{
|
||||
static int value = 0;
|
||||
return value;
|
||||
}
|
||||
|
||||
static auto mismatched_deallocation_count() -> int&
|
||||
{
|
||||
static int value = 0;
|
||||
return value;
|
||||
}
|
||||
};
|
||||
|
||||
template <typename T, typename U>
|
||||
constexpr auto operator==(const OwnershipTrackingAllocator<T>& lhs,
|
||||
const OwnershipTrackingAllocator<U>& rhs) noexcept
|
||||
{
|
||||
return lhs.owner() == rhs.owner();
|
||||
}
|
||||
|
||||
template <typename T, typename U>
|
||||
constexpr auto operator!=(const OwnershipTrackingAllocator<T>& lhs,
|
||||
const OwnershipTrackingAllocator<U>& rhs) noexcept
|
||||
{
|
||||
return !(lhs == rhs);
|
||||
}
|
||||
|
||||
TEST(dyn_array_tests, custom_allocator_models_allocator)
|
||||
{
|
||||
using traits = std::allocator_traits<Newocator<char>>;
|
||||
using ptr = traits::pointer;
|
||||
|
||||
static_assert(std::is_same<traits::value_type, char>::value, "allocator trait type mismatch");
|
||||
static_assert(std::is_same<ptr, char*>::value, "allocator trait type mismatch");
|
||||
|
||||
Newocator<char> alloc;
|
||||
auto p = traits::allocate(alloc, 1);
|
||||
traits::deallocate(alloc, p, 1);
|
||||
|
||||
#if defined(__cpp_lib_constexpr_dynamic_alloc) && (__cpp_lib_constexpr_dynamic_alloc >= 201907L)
|
||||
using constexpr_traits = std::allocator_traits<ConstexprAllocator<char, 10>>;
|
||||
static_assert(std::is_same<constexpr_traits::value_type, char>::value,
|
||||
"allocator trait type mismatch");
|
||||
#endif /* __cpp_lib_constexpr_dynamic_alloc >= 201907L */
|
||||
}
|
||||
|
||||
TEST(dyn_array_tests, custom_allocator)
|
||||
{
|
||||
#if defined(__cpp_lib_constexpr_dynamic_alloc) && (__cpp_lib_constexpr_dynamic_alloc >= 201907L)
|
||||
static constexpr gsl::dyn_array<char, ConstexprAllocator<char, 10>> mets(10, 'c');
|
||||
static_assert(mets.size() == 10);
|
||||
static_assert(mets[0] == 'c');
|
||||
static_assert(std::all_of(std::begin(mets), std::end(mets), [](char c) { return c == 'c'; }));
|
||||
#endif /* __cpp_lib_constexpr_dynamic_alloc >= 201907L */
|
||||
|
||||
Newocator<char>::init();
|
||||
{
|
||||
gsl::dyn_array<char, Newocator<char>> yankees(10, 'c');
|
||||
EXPECT_EQ(yankees.size(), 10);
|
||||
EXPECT_TRUE(
|
||||
std::all_of(std::begin(yankees), std::end(yankees), [](char c) { return c == 'c'; }));
|
||||
yankees[0] = 'a';
|
||||
yankees[1] = 'b';
|
||||
EXPECT_EQ(yankees[0], 'a');
|
||||
EXPECT_EQ(yankees[1], 'b');
|
||||
EXPECT_EQ(yankees[2], 'c');
|
||||
yankees.get_allocator().deallocate(yankees.get_allocator().allocate(1), 1);
|
||||
}
|
||||
Newocator<char>::check();
|
||||
}
|
||||
|
||||
TEST(dyn_array_tests, non_trivial_elements_are_destroyed)
|
||||
{
|
||||
LifetimeCounter::alive_count = 0;
|
||||
|
||||
{
|
||||
gsl::dyn_array<LifetimeCounter> values(5, LifetimeCounter{7});
|
||||
EXPECT_EQ(values.size(), 5);
|
||||
EXPECT_EQ(LifetimeCounter::alive_count, 5);
|
||||
}
|
||||
|
||||
EXPECT_EQ(LifetimeCounter::alive_count, 0);
|
||||
}
|
||||
|
||||
TEST(dyn_array_tests, count_constructor_default_constructs_each_element)
|
||||
{
|
||||
DefaultConstructionCounter::reset();
|
||||
|
||||
{
|
||||
gsl::dyn_array<DefaultConstructionCounter> values(4);
|
||||
EXPECT_EQ(values.size(), 4);
|
||||
}
|
||||
|
||||
EXPECT_EQ(DefaultConstructionCounter::default_constructor_count, 4);
|
||||
EXPECT_EQ(DefaultConstructionCounter::copy_constructor_count, 0);
|
||||
}
|
||||
|
||||
#ifdef GSL_DYN_ARRAY_COMPILE_FAILURE_TESTS
|
||||
TEST(dyn_array_compile_failure_tests, count_constructor_accepts_default_constructible_only_elements)
|
||||
{
|
||||
gsl::dyn_array<DefaultOnlyElement> values(4);
|
||||
EXPECT_EQ(values.size(), 4);
|
||||
}
|
||||
#endif /* GSL_DYN_ARRAY_COMPILE_FAILURE_TESTS */
|
||||
|
||||
TEST(dyn_array_tests, failed_element_construction_rolls_back)
|
||||
{
|
||||
ThrowOnCopy::alive_count = 0;
|
||||
ThrowOnCopy::copy_count = 0;
|
||||
ThrowOnCopy::throw_on_copy_index = 2;
|
||||
|
||||
EXPECT_THROW((gsl::dyn_array<ThrowOnCopy>(5, ThrowOnCopy{1})), int);
|
||||
EXPECT_EQ(ThrowOnCopy::alive_count, 0);
|
||||
|
||||
ThrowOnCopy::throw_on_copy_index = -1;
|
||||
}
|
||||
|
||||
TEST(dyn_array_tests, init_list)
|
||||
{
|
||||
gsl::dyn_array<char> phillies = {'a', 'b', 'c'};
|
||||
EXPECT_EQ(phillies.size(), 3);
|
||||
EXPECT_EQ(phillies[0], 'a');
|
||||
EXPECT_EQ(phillies[1], 'b');
|
||||
EXPECT_EQ(phillies[2], 'c');
|
||||
}
|
||||
|
||||
TEST(dyn_array_tests, const_operations)
|
||||
{
|
||||
const gsl::dyn_array<char> pirates{'a', 'b', 'c', 'd'};
|
||||
EXPECT_EQ(pirates.size(), 4);
|
||||
EXPECT_EQ(pirates[0], 'a');
|
||||
}
|
||||
|
||||
TEST(dyn_array_tests, reverse_iterator)
|
||||
{
|
||||
const gsl::dyn_array<char> padres{'a', 'b', 'c'};
|
||||
auto it = std::rbegin(padres);
|
||||
EXPECT_EQ(*it++, 'c');
|
||||
EXPECT_EQ(*it++, 'b');
|
||||
EXPECT_EQ(*it++, 'a');
|
||||
}
|
||||
|
||||
TEST(dyn_array_tests, random_access_iterator_arithmetic)
|
||||
{
|
||||
gsl::dyn_array<char> bluejays{'a', 'b', 'c', 'd'};
|
||||
|
||||
auto first = bluejays.begin();
|
||||
auto third = first + 2;
|
||||
|
||||
EXPECT_EQ(*third, 'c');
|
||||
EXPECT_EQ(third - first, 2);
|
||||
EXPECT_EQ(*(third - 1), 'b');
|
||||
EXPECT_EQ(*std::prev(third), 'b');
|
||||
}
|
||||
|
||||
TEST(dyn_array_tests, random_access_iterator_arithmetic_accepts_negative_offsets)
|
||||
{
|
||||
gsl::dyn_array<char> bluejays{'a', 'b', 'c', 'd'};
|
||||
|
||||
auto third = bluejays.begin() + 2;
|
||||
char previous{};
|
||||
char next{};
|
||||
|
||||
EXPECT_NO_THROW(previous = *(third + -1));
|
||||
EXPECT_EQ(previous, 'b');
|
||||
|
||||
EXPECT_NO_THROW(next = *(third - -1));
|
||||
EXPECT_EQ(next, 'd');
|
||||
}
|
||||
|
||||
TEST(dyn_array_tests, input_iterator_constructor)
|
||||
{
|
||||
std::istringstream stream{"n a t s"};
|
||||
std::istream_iterator<char> first{stream};
|
||||
const std::istream_iterator<char> last{};
|
||||
|
||||
gsl::dyn_array<char> nationals(first, last);
|
||||
|
||||
ASSERT_EQ(nationals.size(), 4);
|
||||
EXPECT_EQ(nationals[0], 'n');
|
||||
EXPECT_EQ(nationals[1], 'a');
|
||||
EXPECT_EQ(nationals[2], 't');
|
||||
EXPECT_EQ(nationals[3], 's');
|
||||
}
|
||||
|
||||
TEST(dyn_array_tests, contract_violations)
|
||||
{
|
||||
const auto terminateHandler = std::set_terminate([] {
|
||||
std::cerr << "Expected Death. dyn_array_contract_violations";
|
||||
std::abort();
|
||||
});
|
||||
const auto expected = GetExpectedDeathString(terminateHandler);
|
||||
|
||||
gsl::dyn_array<char> values(3, 'v');
|
||||
gsl::dyn_array<char> other(3, 'o');
|
||||
|
||||
EXPECT_DEATH(values[values.size()], expected);
|
||||
EXPECT_DEATH((void) *values.end(), expected);
|
||||
EXPECT_DEATH(++values.end(), expected);
|
||||
EXPECT_DEATH(--values.begin(), expected);
|
||||
EXPECT_DEATH((void) (values.begin() == other.begin()), expected);
|
||||
}
|
||||
|
||||
#ifdef _MSC_VER
|
||||
TEST(dyn_array_tests, unchecked_iterators)
|
||||
{
|
||||
gsl::dyn_array<char> values;
|
||||
const gsl::dyn_array<char> const_values(3, 'v');
|
||||
|
||||
EXPECT_TRUE((std::is_same<decltype(const_values._Unchecked_begin()), const char*>::value));
|
||||
EXPECT_TRUE((std::is_same<decltype(const_values._Unchecked_end()), const char*>::value));
|
||||
|
||||
std::size_t count = 0;
|
||||
for (const auto value : const_values)
|
||||
{
|
||||
EXPECT_EQ(value, 'v');
|
||||
++count;
|
||||
}
|
||||
EXPECT_EQ(count, const_values.size());
|
||||
|
||||
EXPECT_EQ(values._Unchecked_begin(), nullptr);
|
||||
EXPECT_EQ(values._Unchecked_end(), nullptr);
|
||||
}
|
||||
#endif /* _MSC_VER */
|
||||
|
||||
TEST(DynArrayTests, TypeConsistency)
|
||||
{
|
||||
static_assert(std::is_same<gsl::dyn_array<int>::value_type, int>::value, "Value type mismatch");
|
||||
static_assert(std::is_same<gsl::dyn_array<int>::reference, int&>::value,
|
||||
"Reference type mismatch");
|
||||
static_assert(std::is_same<gsl::dyn_array<int>::const_reference, const int&>::value,
|
||||
"Const reference type mismatch");
|
||||
static_assert(std::is_same<gsl::dyn_array<int>::iterator::value_type, int>::value,
|
||||
"Iterator value type mismatch");
|
||||
static_assert(std::is_same<gsl::dyn_array<int>::iterator::reference, int&>::value,
|
||||
"Iterator reference type mismatch");
|
||||
static_assert(std::is_same<gsl::dyn_array<int>::iterator::const_reference, const int&>::value,
|
||||
"Iterator const reference type mismatch");
|
||||
static_assert(std::is_same<gsl::dyn_array<int>::size_type, std::size_t>::value,
|
||||
"Size type mismatch");
|
||||
static_assert(std::is_same<gsl::dyn_array<int>::difference_type, std::ptrdiff_t>::value,
|
||||
"Difference type mismatch");
|
||||
}
|
||||
|
||||
#if defined(__cpp_deduction_guides) && (__cpp_deduction_guides >= 201703L)
|
||||
TEST(dyn_array_tests, deduction_guides)
|
||||
{
|
||||
std::vector<char> giants{10};
|
||||
#if defined(__cpp_lib_containers_ranges) && (__cpp_lib_containers_ranges >= 202202L)
|
||||
gsl::dyn_array mariners(std::from_range, giants);
|
||||
#endif /* __cpp_lib_containers_ranges >= 202202L */
|
||||
gsl::dyn_array cardinals(std::begin(giants), std::end(giants));
|
||||
}
|
||||
#endif /* __cpp_deduction_guides >= 201703L */
|
||||
|
|
@ -32,14 +32,14 @@
|
|||
#include <vector> // for vector
|
||||
|
||||
// the string_view include and macro are used in the deduction guide verification
|
||||
#if (defined(__cpp_deduction_guides) && (__cpp_deduction_guides >= 201611L))
|
||||
#if defined(__cpp_deduction_guides) && (__cpp_deduction_guides >= 201703L)
|
||||
#ifdef __has_include
|
||||
#if __has_include(<string_view>)
|
||||
#include <string_view>
|
||||
#define HAS_STRING_VIEW
|
||||
#endif // __has_include(<string_view>)
|
||||
#endif // __has_include
|
||||
#endif // (defined(__cpp_deduction_guides) && (__cpp_deduction_guides >= 201611L))
|
||||
#endif // defined(__cpp_deduction_guides) && (__cpp_deduction_guides >= 201703L)
|
||||
#if defined(__cplusplus) && __cplusplus >= 202002L
|
||||
#include <span>
|
||||
#endif // __cplusplus >= 202002L
|
||||
|
|
@ -1211,7 +1211,7 @@ TEST(span_test, default_constructible)
|
|||
|
||||
TEST(span_test, std_container_ctad)
|
||||
{
|
||||
#if (defined(__cpp_deduction_guides) && (__cpp_deduction_guides >= 201611L))
|
||||
#if defined(__cpp_deduction_guides) && (__cpp_deduction_guides >= 201703L)
|
||||
// this test is just to verify that these compile
|
||||
{
|
||||
std::vector<int> v{1, 2, 3, 4};
|
||||
|
|
@ -1230,7 +1230,7 @@ TEST(span_test, std_container_ctad)
|
|||
static_assert(std::is_same<decltype(sp), gsl::span<const char>>::value);
|
||||
}
|
||||
#endif
|
||||
#endif
|
||||
#endif // defined(__cpp_deduction_guides) && (__cpp_deduction_guides >= 201703L)
|
||||
}
|
||||
|
||||
TEST(span_test, front_back)
|
||||
|
|
@ -1296,13 +1296,13 @@ TEST(span_test, conversions)
|
|||
{
|
||||
int arr[5] = {1, 2, 3, 4, 5};
|
||||
|
||||
#if defined(__cpp_deduction_guides) && (__cpp_deduction_guides >= 201611L)
|
||||
#if defined(__cpp_deduction_guides) && (__cpp_deduction_guides >= 201703L)
|
||||
span s = arr;
|
||||
span cs = s;
|
||||
#else
|
||||
#else // ^^^ deduction guides /// no deduction guides vvv
|
||||
span<int, 5> s = arr;
|
||||
span<int, 5> cs = s;
|
||||
#endif
|
||||
#endif // defined(__cpp_deduction_guides) && (__cpp_deduction_guides >= 201703L)
|
||||
|
||||
EXPECT_TRUE(cs.size() == s.size());
|
||||
EXPECT_TRUE(cs.data() == s.data());
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue