зеркало из https://github.com/microsoft/STL.git
Merge pull request #5055 from StephanTLavavej/merge-flat_map
Merge `main` to `feature/flat_map`
This commit is contained in:
Коммит
fbe5394250
|
@ -0,0 +1,11 @@
|
|||
# Copyright (c) Microsoft Corporation.
|
||||
# SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
|
||||
|
||||
# This file maps author and committer email addresses to canonical names and email
|
||||
# addresses for display in the output of `git log` and `git blame`.
|
||||
# Format:
|
||||
# Canonical Name <canonical@example.com> <commit@example.com>
|
||||
|
||||
|
||||
Amy Wishnousky <amyw@microsoft.com> <stwish@microsoft.com>
|
||||
Anju del Moral Gonzalez <delMoralGonzalez.Anju@microsoft.com> <judelmor@microsoft.com>
|
22
README.md
22
README.md
|
@ -433,6 +433,26 @@ build folder to your path:
|
|||
set PATH=C:\STL\out\x64\out\bin\amd64;%PATH%
|
||||
```
|
||||
|
||||
## Running Tests With Address Sanitizer (ASan)
|
||||
|
||||
You don't need any extra steps to run with test code and the code in STL headers instrumented with [ASan][].
|
||||
The test matrices include both ASan and non-ASan configurations.
|
||||
|
||||
However, to instrument the separately-compiled code (the DLL, the satellites, the [Import Library][] - everything that's
|
||||
in `/stl/src`), you need to build the STL with ASan. Change the build steps to add `-DSTL_ASAN_BUILD=ON`:
|
||||
|
||||
```
|
||||
cmake --preset x64 -DSTL_ASAN_BUILD=ON
|
||||
cmake --build --preset x64
|
||||
```
|
||||
|
||||
ASan-instrumented STL binaries require that the executable be instrumented as well, so you'll have to skip the non-ASan
|
||||
configurations by passing `-Dtags=ASAN` to `stl-lit.py`:
|
||||
|
||||
```
|
||||
python tests\utils\stl-lit\stl-lit.py ..\..\tests\std\tests\VSO_0000000_vector_algorithms -Dtags=ASAN -v
|
||||
```
|
||||
|
||||
# Benchmarking
|
||||
|
||||
For performance-sensitive code – containers, algorithms, and the like –
|
||||
|
@ -590,3 +610,5 @@ SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
|
|||
[lit result codes]: https://llvm.org/docs/CommandGuide/lit.html#test-status-results
|
||||
[redistributables]: https://learn.microsoft.com/en-US/cpp/windows/latest-supported-vc-redist
|
||||
[natvis documentation]: https://learn.microsoft.com/en-us/visualstudio/debugger/create-custom-views-of-native-objects
|
||||
[ASan]: https://learn.microsoft.com/en-us/cpp/sanitizers/asan
|
||||
[Import Library]: /docs/import_library.md
|
||||
|
|
|
@ -106,6 +106,7 @@ function(add_benchmark name)
|
|||
target_link_libraries(benchmark-${name} PRIVATE benchmark::benchmark)
|
||||
endfunction()
|
||||
|
||||
add_benchmark(adjacent_difference src/adjacent_difference.cpp)
|
||||
add_benchmark(bitset_from_string src/bitset_from_string.cpp)
|
||||
add_benchmark(bitset_to_string src/bitset_to_string.cpp)
|
||||
add_benchmark(efficient_nonlocking_print src/efficient_nonlocking_print.cpp)
|
||||
|
|
|
@ -0,0 +1,76 @@
|
|||
// Copyright (c) Microsoft Corporation.
|
||||
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <cstddef>
|
||||
#include <cstdlib>
|
||||
#include <new>
|
||||
|
||||
template <class T, size_t Alignment, size_t Skew>
|
||||
struct skewed_allocator {
|
||||
using value_type = T;
|
||||
static_assert(Alignment % alignof(T) == 0, "Chosen Alignment will produce unaligned T objects");
|
||||
static_assert(Skew % alignof(T) == 0, "Chosen Skew will produce unaligned T objects");
|
||||
|
||||
template <class U>
|
||||
struct rebind {
|
||||
using other = skewed_allocator<U, Alignment, Skew>;
|
||||
};
|
||||
|
||||
skewed_allocator() = default;
|
||||
template <class U>
|
||||
skewed_allocator(const skewed_allocator<U, Alignment, Skew>&) {}
|
||||
|
||||
template <class U>
|
||||
bool operator==(const skewed_allocator<U, Alignment, Skew>&) const {
|
||||
return true;
|
||||
}
|
||||
|
||||
T* allocate(const size_t n) {
|
||||
const auto p = static_cast<unsigned char*>(_aligned_malloc(n * sizeof(T) + Skew, Alignment));
|
||||
if (!p) {
|
||||
throw std::bad_alloc{};
|
||||
}
|
||||
return reinterpret_cast<T*>(p + Skew);
|
||||
}
|
||||
|
||||
void deallocate(T* const p, size_t) {
|
||||
if (p) {
|
||||
_aligned_free(reinterpret_cast<unsigned char*>(p) - Skew);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
// The purpose is to provide consistent behavior for benchmarks.
|
||||
// 64 would be a reasonable alignment for practical perf uses,
|
||||
// as it is both the cache line size and the maximum vector instruction size (on x64).
|
||||
// However, aligning to the page size will provide even more consistency
|
||||
// by ensuring that the same number of page boundaries is crossed each time.
|
||||
inline constexpr size_t page_size = 4096;
|
||||
|
||||
// A realistic skew relative to allocation granularity, when a variable is placed
|
||||
// next to a pointer in a structure or on the stack. Also corresponds to the default packing.
|
||||
inline constexpr size_t realistic_skew = 8;
|
||||
|
||||
template <class T>
|
||||
using highly_aligned_allocator = skewed_allocator<T, page_size, 0>;
|
||||
|
||||
template <class T>
|
||||
using not_highly_aligned_allocator = skewed_allocator<T, page_size, realistic_skew>;
|
||||
|
||||
#pragma warning(push)
|
||||
#pragma warning(disable : 4324) // structure was padded due to alignment specifier
|
||||
|
||||
template <class T>
|
||||
struct alignas(page_size) highly_aligned {
|
||||
T value;
|
||||
};
|
||||
|
||||
template <class T>
|
||||
struct alignas(page_size) not_highly_aligned {
|
||||
char pad[realistic_skew];
|
||||
T value;
|
||||
};
|
||||
|
||||
#pragma warning(pop)
|
|
@ -0,0 +1,57 @@
|
|||
// Copyright (c) Microsoft Corporation.
|
||||
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
|
||||
|
||||
#include <algorithm>
|
||||
#include <benchmark/benchmark.h>
|
||||
#include <cstddef>
|
||||
#include <cstdint>
|
||||
#include <limits>
|
||||
#include <numeric>
|
||||
#include <random>
|
||||
#include <type_traits>
|
||||
#include <vector>
|
||||
|
||||
using namespace std;
|
||||
|
||||
template <class T>
|
||||
void bm(benchmark::State& state) {
|
||||
mt19937 gen(96337);
|
||||
|
||||
const size_t size = static_cast<size_t>(state.range(0));
|
||||
|
||||
vector<T> input(size);
|
||||
vector<T> output(size);
|
||||
|
||||
if constexpr (is_floating_point_v<T>) {
|
||||
normal_distribution<T> dis(-100.0, 100.0);
|
||||
ranges::generate(input, [&] { return dis(gen); });
|
||||
} else {
|
||||
static_assert(is_unsigned_v<T>, "This avoids signed integers to avoid UB; they shouldn't perform differently");
|
||||
uniform_int_distribution<conditional_t<sizeof(T) != 1, T, unsigned int>> dis(0, numeric_limits<T>::max());
|
||||
ranges::generate(input, [&] { return static_cast<T>(dis(gen)); });
|
||||
}
|
||||
|
||||
for (auto _ : state) {
|
||||
benchmark::DoNotOptimize(input);
|
||||
adjacent_difference(input.begin(), input.end(), output.begin());
|
||||
benchmark::DoNotOptimize(output);
|
||||
}
|
||||
}
|
||||
|
||||
void common_args(auto bm) {
|
||||
bm->Arg(2255);
|
||||
}
|
||||
|
||||
#pragma warning(push)
|
||||
#pragma warning(disable : 4244) // warning C4244: '=': conversion from 'int' to 'unsigned char', possible loss of data
|
||||
BENCHMARK(bm<uint8_t>)->Apply(common_args);
|
||||
BENCHMARK(bm<uint16_t>)->Apply(common_args);
|
||||
#pragma warning(pop)
|
||||
|
||||
BENCHMARK(bm<uint32_t>)->Apply(common_args);
|
||||
BENCHMARK(bm<uint64_t>)->Apply(common_args);
|
||||
|
||||
BENCHMARK(bm<float>)->Apply(common_args);
|
||||
BENCHMARK(bm<double>)->Apply(common_args);
|
||||
|
||||
BENCHMARK_MAIN();
|
|
@ -111,6 +111,22 @@ void search_default_searcher(benchmark::State& state) {
|
|||
}
|
||||
}
|
||||
|
||||
template <class T>
|
||||
void member_find(benchmark::State& state) {
|
||||
const auto& src_haystack = patterns[static_cast<size_t>(state.range())].data;
|
||||
const auto& src_needle = patterns[static_cast<size_t>(state.range())].pattern;
|
||||
|
||||
const T haystack(src_haystack.begin(), src_haystack.end());
|
||||
const T needle(src_needle.begin(), src_needle.end());
|
||||
|
||||
for (auto _ : state) {
|
||||
benchmark::DoNotOptimize(haystack);
|
||||
benchmark::DoNotOptimize(needle);
|
||||
auto res = haystack.find(needle);
|
||||
benchmark::DoNotOptimize(res);
|
||||
}
|
||||
}
|
||||
|
||||
template <class T>
|
||||
void classic_find_end(benchmark::State& state) {
|
||||
const auto& src_haystack = patterns[static_cast<size_t>(state.range())].data;
|
||||
|
@ -158,6 +174,9 @@ BENCHMARK(ranges_search<std::uint16_t>)->Apply(common_args);
|
|||
BENCHMARK(search_default_searcher<std::uint8_t>)->Apply(common_args);
|
||||
BENCHMARK(search_default_searcher<std::uint16_t>)->Apply(common_args);
|
||||
|
||||
BENCHMARK(member_find<std::string>)->Apply(common_args);
|
||||
BENCHMARK(member_find<std::wstring>)->Apply(common_args);
|
||||
|
||||
BENCHMARK(classic_find_end<std::uint8_t>)->Apply(common_args);
|
||||
BENCHMARK(classic_find_end<std::uint16_t>)->Apply(common_args);
|
||||
|
||||
|
|
|
@ -7,13 +7,17 @@
|
|||
#include <cstdint>
|
||||
#include <vector>
|
||||
|
||||
#include "skewed_allocator.hpp"
|
||||
|
||||
using namespace std;
|
||||
|
||||
template <size_t N, class T>
|
||||
template <size_t N, class T, template <class> class Padder>
|
||||
void std_swap(benchmark::State& state) {
|
||||
T a[N];
|
||||
Padder<T[N]> padded_a;
|
||||
auto& a = padded_a.value;
|
||||
memset(a, 'a', sizeof(a));
|
||||
T b[N];
|
||||
Padder<T[N]> padded_b;
|
||||
auto& b = padded_b.value;
|
||||
memset(b, 'b', sizeof(b));
|
||||
|
||||
for (auto _ : state) {
|
||||
|
@ -23,10 +27,10 @@ void std_swap(benchmark::State& state) {
|
|||
}
|
||||
}
|
||||
|
||||
template <class T>
|
||||
template <class T, template <class> class Alloc>
|
||||
void std_swap_ranges(benchmark::State& state) {
|
||||
vector<T> a(static_cast<size_t>(state.range(0)), T{'a'});
|
||||
vector<T> b(static_cast<size_t>(state.range(0)), T{'b'});
|
||||
vector<T, Alloc<T>> a(static_cast<size_t>(state.range(0)), T{'a'});
|
||||
vector<T, Alloc<T>> b(static_cast<size_t>(state.range(0)), T{'b'});
|
||||
|
||||
for (auto _ : state) {
|
||||
swap_ranges(a.begin(), a.end(), b.begin());
|
||||
|
@ -35,18 +39,41 @@ void std_swap_ranges(benchmark::State& state) {
|
|||
}
|
||||
}
|
||||
|
||||
BENCHMARK(std_swap<1, uint8_t>);
|
||||
BENCHMARK(std_swap<5, uint8_t>);
|
||||
BENCHMARK(std_swap<15, uint8_t>);
|
||||
BENCHMARK(std_swap<26, uint8_t>);
|
||||
BENCHMARK(std_swap<38, uint8_t>);
|
||||
BENCHMARK(std_swap<60, uint8_t>);
|
||||
BENCHMARK(std_swap<125, uint8_t>);
|
||||
BENCHMARK(std_swap<800, uint8_t>);
|
||||
BENCHMARK(std_swap<3000, uint8_t>);
|
||||
BENCHMARK(std_swap<9000, uint8_t>);
|
||||
BENCHMARK(std_swap<1, uint8_t, highly_aligned>);
|
||||
BENCHMARK(std_swap<5, uint8_t, highly_aligned>);
|
||||
BENCHMARK(std_swap<15, uint8_t, highly_aligned>);
|
||||
BENCHMARK(std_swap<26, uint8_t, highly_aligned>);
|
||||
BENCHMARK(std_swap<38, uint8_t, highly_aligned>);
|
||||
BENCHMARK(std_swap<60, uint8_t, highly_aligned>);
|
||||
BENCHMARK(std_swap<125, uint8_t, highly_aligned>);
|
||||
BENCHMARK(std_swap<800, uint8_t, highly_aligned>);
|
||||
BENCHMARK(std_swap<3000, uint8_t, highly_aligned>);
|
||||
BENCHMARK(std_swap<9000, uint8_t, highly_aligned>);
|
||||
|
||||
BENCHMARK(std_swap_ranges<uint8_t>)
|
||||
BENCHMARK(std_swap<1, uint8_t, not_highly_aligned>);
|
||||
BENCHMARK(std_swap<5, uint8_t, not_highly_aligned>);
|
||||
BENCHMARK(std_swap<15, uint8_t, not_highly_aligned>);
|
||||
BENCHMARK(std_swap<26, uint8_t, not_highly_aligned>);
|
||||
BENCHMARK(std_swap<38, uint8_t, not_highly_aligned>);
|
||||
BENCHMARK(std_swap<60, uint8_t, not_highly_aligned>);
|
||||
BENCHMARK(std_swap<125, uint8_t, not_highly_aligned>);
|
||||
BENCHMARK(std_swap<800, uint8_t, not_highly_aligned>);
|
||||
BENCHMARK(std_swap<3000, uint8_t, not_highly_aligned>);
|
||||
BENCHMARK(std_swap<9000, uint8_t, not_highly_aligned>);
|
||||
|
||||
BENCHMARK(std_swap_ranges<uint8_t, highly_aligned_allocator>)
|
||||
->Arg(1)
|
||||
->Arg(5)
|
||||
->Arg(15)
|
||||
->Arg(26)
|
||||
->Arg(38)
|
||||
->Arg(60)
|
||||
->Arg(125)
|
||||
->Arg(800)
|
||||
->Arg(3000)
|
||||
->Arg(9000);
|
||||
|
||||
BENCHMARK(std_swap_ranges<uint8_t, not_highly_aligned_allocator>)
|
||||
->Arg(1)
|
||||
->Arg(5)
|
||||
->Arg(15)
|
||||
|
|
|
@ -1 +1 @@
|
|||
Subproject commit 886b76128fba5f995c8c8e24aaa2030b59dec01a
|
||||
Subproject commit 69ead949d08ff0bb8cbbf4f7143aaa6687830f6b
|
|
@ -628,6 +628,21 @@ constexpr size_t _Traits_find(_In_reads_(_Hay_size) const _Traits_ptr_t<_Traits>
|
|||
return _Start_at;
|
||||
}
|
||||
|
||||
#if _USE_STD_VECTOR_ALGORITHMS
|
||||
if constexpr (_Is_implementation_handled_char_traits<_Traits> && sizeof(typename _Traits::char_type) <= 2) {
|
||||
if (!_STD _Is_constant_evaluated()) {
|
||||
const auto _End = _Haystack + _Hay_size;
|
||||
const auto _Ptr = _STD _Search_vectorized(_Haystack + _Start_at, _End, _Needle, _Needle_size);
|
||||
|
||||
if (_Ptr != _End) {
|
||||
return static_cast<size_t>(_Ptr - _Haystack);
|
||||
} else {
|
||||
return static_cast<size_t>(-1);
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif // _USE_STD_VECTOR_ALGORITHMS
|
||||
|
||||
const auto _Possible_matches_end = _Haystack + (_Hay_size - _Needle_size) + 1;
|
||||
for (auto _Match_try = _Haystack + _Start_at;; ++_Match_try) {
|
||||
_Match_try = _Traits::find(_Match_try, static_cast<size_t>(_Possible_matches_end - _Match_try), *_Needle);
|
||||
|
@ -1756,13 +1771,14 @@ _NODISCARD constexpr bool operator==(const basic_string_view<_Elem, _Traits> _Lh
|
|||
return _Lhs._Equal(_Rhs);
|
||||
}
|
||||
|
||||
template <class _Traits, class = void>
|
||||
template <class _Traits>
|
||||
struct _Get_comparison_category {
|
||||
using type = weak_ordering;
|
||||
};
|
||||
|
||||
template <class _Traits>
|
||||
struct _Get_comparison_category<_Traits, void_t<typename _Traits::comparison_category>> {
|
||||
requires requires { typename _Traits::comparison_category; }
|
||||
struct _Get_comparison_category<_Traits> {
|
||||
using type = _Traits::comparison_category;
|
||||
|
||||
static_assert(_Is_any_of_v<type, partial_ordering, weak_ordering, strong_ordering>,
|
||||
|
|
|
@ -33,13 +33,12 @@ struct _Stl_critical_section {
|
|||
};
|
||||
|
||||
struct _Mtx_internal_imp_t {
|
||||
#if defined(_CRT_WINDOWS) || defined(UNDOCKED_WINDOWS_UCRT) // for Windows-internal code
|
||||
static constexpr size_t _Critical_section_size = 2 * sizeof(void*);
|
||||
#elif defined(_WIN64) // ordinary 64-bit code
|
||||
// TRANSITION, ABI: We should directly store _M_srw_lock above.
|
||||
#ifdef _WIN64
|
||||
static constexpr size_t _Critical_section_size = 64;
|
||||
#else // vvv ordinary 32-bit code vvv
|
||||
#else // ^^^ 64-bit / 32-bit vvv
|
||||
static constexpr size_t _Critical_section_size = 36;
|
||||
#endif // ^^^ ordinary 32-bit code ^^^
|
||||
#endif // ^^^ 32-bit ^^^
|
||||
|
||||
int _Type{};
|
||||
union {
|
||||
|
@ -60,13 +59,12 @@ struct _Stl_condition_variable {
|
|||
#pragma warning(push)
|
||||
#pragma warning(disable : 26495) // Variable 'meow' is uninitialized. Always initialize a member variable (type.6).
|
||||
struct _Cnd_internal_imp_t {
|
||||
#if defined(_CRT_WINDOWS) // for Windows-internal code
|
||||
static constexpr size_t _Cnd_internal_imp_size = 2 * sizeof(void*);
|
||||
#elif defined(_WIN64) // ordinary 64-bit code
|
||||
// TRANSITION, ABI: We should directly store _Win_cv above.
|
||||
#ifdef _WIN64
|
||||
static constexpr size_t _Cnd_internal_imp_size = 72;
|
||||
#else // vvv ordinary 32-bit code vvv
|
||||
#else // ^^^ 64-bit / 32-bit vvv
|
||||
static constexpr size_t _Cnd_internal_imp_size = 40;
|
||||
#endif // ^^^ ordinary 32-bit code ^^^
|
||||
#endif // ^^^ 32-bit ^^^
|
||||
|
||||
union {
|
||||
_Stl_condition_variable _Stl_cv{};
|
||||
|
|
|
@ -59,6 +59,11 @@ const void* __stdcall __std_find_last_trivial_2(const void* _First, const void*
|
|||
const void* __stdcall __std_find_last_trivial_4(const void* _First, const void* _Last, uint32_t _Val) noexcept;
|
||||
const void* __stdcall __std_find_last_trivial_8(const void* _First, const void* _Last, uint64_t _Val) noexcept;
|
||||
|
||||
const void* __stdcall __std_find_end_1(
|
||||
const void* _First1, const void* _Last1, const void* _First2, size_t _Count2) noexcept;
|
||||
const void* __stdcall __std_find_end_2(
|
||||
const void* _First1, const void* _Last1, const void* _First2, size_t _Count2) noexcept;
|
||||
|
||||
__declspec(noalias) _Min_max_1i __stdcall __std_minmax_1i(const void* _First, const void* _Last) noexcept;
|
||||
__declspec(noalias) _Min_max_1u __stdcall __std_minmax_1u(const void* _First, const void* _Last) noexcept;
|
||||
__declspec(noalias) _Min_max_2i __stdcall __std_minmax_2i(const void* _First, const void* _Last) noexcept;
|
||||
|
@ -189,6 +194,19 @@ _Ty* _Find_last_vectorized(_Ty* const _First, _Ty* const _Last, const _TVal _Val
|
|||
}
|
||||
}
|
||||
|
||||
template <class _Ty1, class _Ty2>
|
||||
_Ty1* _Find_end_vectorized(
|
||||
_Ty1* const _First1, _Ty1* const _Last1, _Ty2* const _First2, const size_t _Count2) noexcept {
|
||||
_STL_INTERNAL_STATIC_ASSERT(sizeof(_Ty1) == sizeof(_Ty2));
|
||||
if constexpr (sizeof(_Ty1) == 1) {
|
||||
return const_cast<_Ty1*>(static_cast<const _Ty1*>(::__std_find_end_1(_First1, _Last1, _First2, _Count2)));
|
||||
} else if constexpr (sizeof(_Ty1) == 2) {
|
||||
return const_cast<_Ty1*>(static_cast<const _Ty1*>(::__std_find_end_2(_First1, _Last1, _First2, _Count2)));
|
||||
} else {
|
||||
_STL_INTERNAL_STATIC_ASSERT(false); // unexpected size
|
||||
}
|
||||
}
|
||||
|
||||
template <class _Ty, class _TVal1, class _TVal2>
|
||||
__declspec(noalias) void _Replace_vectorized(
|
||||
_Ty* const _First, _Ty* const _Last, const _TVal1 _Old_val, const _TVal2 _New_val) noexcept {
|
||||
|
@ -3194,6 +3212,26 @@ _NODISCARD _CONSTEXPR20 _FwdIt1 find_end(
|
|||
if constexpr (_Is_ranges_random_iter_v<_FwdIt1> && _Is_ranges_random_iter_v<_FwdIt2>) {
|
||||
const _Iter_diff_t<_FwdIt2> _Count2 = _ULast2 - _UFirst2;
|
||||
if (_Count2 > 0 && _Count2 <= _ULast1 - _UFirst1) {
|
||||
#if _USE_STD_VECTOR_ALGORITHMS
|
||||
if constexpr (_Vector_alg_in_search_is_safe<decltype(_UFirst1), decltype(_UFirst2), _Pr>) {
|
||||
if (!_STD _Is_constant_evaluated()) {
|
||||
const auto _Ptr1 = _STD _To_address(_UFirst1);
|
||||
|
||||
const auto _Ptr_res1 = _STD _Find_end_vectorized(
|
||||
_Ptr1, _STD _To_address(_ULast1), _STD _To_address(_UFirst2), static_cast<size_t>(_Count2));
|
||||
|
||||
if constexpr (is_pointer_v<decltype(_UFirst1)>) {
|
||||
_UFirst1 = _Ptr_res1;
|
||||
} else {
|
||||
_UFirst1 += _Ptr_res1 - _Ptr1;
|
||||
}
|
||||
|
||||
_STD _Seek_wrapped(_First1, _UFirst1);
|
||||
return _First1;
|
||||
}
|
||||
}
|
||||
#endif // _USE_STD_VECTOR_ALGORITHMS
|
||||
|
||||
for (auto _UCandidate = _ULast1 - static_cast<_Iter_diff_t<_FwdIt1>>(_Count2);; --_UCandidate) {
|
||||
if (_STD _Equal_rev_pred_unchecked(_UCandidate, _UFirst2, _ULast2, _STD _Pass_fn(_Pred))) {
|
||||
_STD _Seek_wrapped(_First1, _UCandidate);
|
||||
|
@ -3297,6 +3335,34 @@ namespace ranges {
|
|||
|
||||
if (_Count2 > 0 && _Count2 <= _Count1) {
|
||||
const auto _Count2_as1 = static_cast<iter_difference_t<_It1>>(_Count2);
|
||||
#if _USE_STD_VECTOR_ALGORITHMS
|
||||
if constexpr (_Vector_alg_in_search_is_safe<_It1, _It2, _Pr> && is_same_v<_Pj1, identity>
|
||||
&& is_same_v<_Pj2, identity>) {
|
||||
if (!_STD is_constant_evaluated()) {
|
||||
const auto _Ptr1 = _STD to_address(_First1);
|
||||
const auto _Ptr2 = _STD to_address(_First2);
|
||||
const auto _Ptr_last1 = _Ptr1 + _Count1;
|
||||
|
||||
const auto _Ptr_res1 =
|
||||
_STD _Find_end_vectorized(_Ptr1, _Ptr_last1, _Ptr2, static_cast<size_t>(_Count2));
|
||||
|
||||
if constexpr (is_pointer_v<_It1>) {
|
||||
if (_Ptr_res1 != _Ptr_last1) {
|
||||
return {_Ptr_res1, _Ptr_res1 + _Count2};
|
||||
} else {
|
||||
return {_Ptr_res1, _Ptr_res1};
|
||||
}
|
||||
} else {
|
||||
_First1 += _Ptr_res1 - _Ptr1;
|
||||
if (_Ptr_res1 != _Ptr_last1) {
|
||||
return {_First1, _First1 + _Count2_as1};
|
||||
} else {
|
||||
return {_First1, _First1};
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif // _USE_STD_VECTOR_ALGORITHMS
|
||||
|
||||
for (auto _Candidate = _First1 + (_Count1 - _Count2_as1);; --_Candidate) {
|
||||
auto _Match_and_mid1 =
|
||||
|
|
|
@ -4612,57 +4612,56 @@ namespace chrono {
|
|||
minutes* _Offset;
|
||||
};
|
||||
|
||||
template <class _CharT, class _Traits, class _Parsable, class... _Rest>
|
||||
using _Has_from_stream =
|
||||
decltype(static_cast<void>(from_stream(_STD declval<basic_istream<_CharT, _Traits>&>(),
|
||||
_STD declval<const _CharT*>(), _STD declval<_Parsable&>(), _STD declval<_Rest>()...)),
|
||||
0); // intentional ADL
|
||||
template <class _Parsable, class _CharT, class _Traits, class... _Rest>
|
||||
concept _Can_from_stream = requires(
|
||||
basic_istream<_CharT, _Traits>& __istr, const _CharT* __s, _Parsable& __parsed, _Rest&&... __rest_args) {
|
||||
from_stream(__istr, +__s, __parsed, _STD forward<_Rest>(__rest_args)...); // intentional ADL
|
||||
};
|
||||
|
||||
_EXPORT_STD template <class _CharT, class _Parsable, _Has_from_stream<_CharT, char_traits<_CharT>, _Parsable> = 0>
|
||||
_EXPORT_STD template <class _CharT, _Can_from_stream<_CharT, char_traits<_CharT>> _Parsable>
|
||||
_NODISCARD auto parse(const _CharT* _Fmt, _Parsable& _Tp) {
|
||||
return _Time_parse_iomanip_c_str<_CharT, char_traits<_CharT>, allocator<_CharT>, _Parsable>{_Fmt, _Tp};
|
||||
}
|
||||
|
||||
_EXPORT_STD template <class _CharT, class _Traits, class _Alloc, class _Parsable,
|
||||
_Has_from_stream<_CharT, _Traits, _Parsable> = 0>
|
||||
_EXPORT_STD template <class _CharT, class _Traits, class _Alloc, _Can_from_stream<_CharT, _Traits> _Parsable>
|
||||
_NODISCARD auto parse(const basic_string<_CharT, _Traits, _Alloc>& _Fmt, _Parsable& _Tp) {
|
||||
return _Time_parse_iomanip{_Fmt, _Tp};
|
||||
}
|
||||
|
||||
_EXPORT_STD template <class _CharT, class _Traits, class _Alloc, class _Parsable,
|
||||
_Has_from_stream<_CharT, _Traits, _Parsable, basic_string<_CharT, _Traits, _Alloc>*> = 0>
|
||||
_EXPORT_STD template <class _CharT, class _Traits, class _Alloc,
|
||||
_Can_from_stream<_CharT, _Traits, basic_string<_CharT, _Traits, _Alloc>*> _Parsable>
|
||||
_NODISCARD auto parse(const _CharT* _Fmt, _Parsable& _Tp, basic_string<_CharT, _Traits, _Alloc>& _Abbrev) {
|
||||
return _Time_parse_iomanip_c_str{_Fmt, _Tp, _STD addressof(_Abbrev)};
|
||||
}
|
||||
|
||||
_EXPORT_STD template <class _CharT, class _Traits, class _Alloc, class _Parsable,
|
||||
_Has_from_stream<_CharT, _Traits, _Parsable, basic_string<_CharT, _Traits, _Alloc>*> = 0>
|
||||
_EXPORT_STD template <class _CharT, class _Traits, class _Alloc,
|
||||
_Can_from_stream<_CharT, _Traits, basic_string<_CharT, _Traits, _Alloc>*> _Parsable>
|
||||
_NODISCARD auto parse(const basic_string<_CharT, _Traits, _Alloc>& _Fmt, _Parsable& _Tp,
|
||||
basic_string<_CharT, _Traits, _Alloc>& _Abbrev) {
|
||||
return _Time_parse_iomanip{_Fmt, _Tp, _STD addressof(_Abbrev)};
|
||||
}
|
||||
|
||||
_EXPORT_STD template <class _CharT, class _Parsable,
|
||||
_Has_from_stream<_CharT, char_traits<_CharT>, _Parsable, basic_string<_CharT>*, minutes*> = 0>
|
||||
_EXPORT_STD template <class _CharT,
|
||||
_Can_from_stream<_CharT, char_traits<_CharT>, basic_string<_CharT>*, minutes*> _Parsable>
|
||||
_NODISCARD auto parse(const _CharT* _Fmt, _Parsable& _Tp, minutes& _Offset) {
|
||||
return _Time_parse_iomanip_c_str{_Fmt, _Tp, static_cast<basic_string<_CharT>*>(nullptr), &_Offset};
|
||||
}
|
||||
|
||||
_EXPORT_STD template <class _CharT, class _Traits, class _Alloc, class _Parsable,
|
||||
_Has_from_stream<_CharT, _Traits, _Parsable, basic_string<_CharT, _Traits, _Alloc>*, minutes*> = 0>
|
||||
_EXPORT_STD template <class _CharT, class _Traits, class _Alloc,
|
||||
_Can_from_stream<_CharT, _Traits, basic_string<_CharT, _Traits, _Alloc>*, minutes*> _Parsable>
|
||||
_NODISCARD auto parse(const basic_string<_CharT, _Traits, _Alloc>& _Fmt, _Parsable& _Tp, minutes& _Offset) {
|
||||
return _Time_parse_iomanip{_Fmt, _Tp, static_cast<basic_string<_CharT, _Traits, _Alloc>*>(nullptr), &_Offset};
|
||||
}
|
||||
|
||||
_EXPORT_STD template <class _CharT, class _Traits, class _Alloc, class _Parsable,
|
||||
_Has_from_stream<_CharT, _Traits, _Parsable, basic_string<_CharT, _Traits, _Alloc>*, minutes*> = 0>
|
||||
_EXPORT_STD template <class _CharT, class _Traits, class _Alloc,
|
||||
_Can_from_stream<_CharT, _Traits, basic_string<_CharT, _Traits, _Alloc>*, minutes*> _Parsable>
|
||||
_NODISCARD auto parse(
|
||||
const _CharT* _Fmt, _Parsable& _Tp, basic_string<_CharT, _Traits, _Alloc>& _Abbrev, minutes& _Offset) {
|
||||
return _Time_parse_iomanip_c_str{_Fmt, _Tp, _STD addressof(_Abbrev), &_Offset};
|
||||
}
|
||||
|
||||
_EXPORT_STD template <class _CharT, class _Traits, class _Alloc, class _Parsable,
|
||||
_Has_from_stream<_CharT, _Traits, _Parsable, basic_string<_CharT, _Traits, _Alloc>*, minutes*> = 0>
|
||||
_EXPORT_STD template <class _CharT, class _Traits, class _Alloc,
|
||||
_Can_from_stream<_CharT, _Traits, basic_string<_CharT, _Traits, _Alloc>*, minutes*> _Parsable>
|
||||
_NODISCARD auto parse(const basic_string<_CharT, _Traits, _Alloc>& _Fmt, _Parsable& _Tp,
|
||||
basic_string<_CharT, _Traits, _Alloc>& _Abbrev, minutes& _Offset) {
|
||||
return _Time_parse_iomanip{_Fmt, _Tp, _STD addressof(_Abbrev), &_Offset};
|
||||
|
|
|
@ -461,6 +461,20 @@ _FwdIt2 transform_inclusive_scan(_ExPo&& _Exec, _FwdIt1 _First, _FwdIt1 _Last, _
|
|||
_UnaryOp _Transform_op) noexcept; // terminates
|
||||
#endif // _HAS_CXX17
|
||||
|
||||
template <class _TyDest, class _TySrc, class _BinOp>
|
||||
void _Adjacent_difference_no_overlap(
|
||||
_TyDest* const __restrict _Dest, _TySrc* const __restrict _Src, const ptrdiff_t _Count, _BinOp _Func) {
|
||||
_Dest[0] = _Src[0];
|
||||
for (ptrdiff_t _Ix = 1; _Ix != _Count; ++_Ix) {
|
||||
#if _HAS_CXX20
|
||||
_TySrc _Tmp = _Src[_Ix - 1];
|
||||
_Dest[_Ix] = _Func(_Src[_Ix], _STD move(_Tmp));
|
||||
#else // ^^^ _HAS_CXX20 / !_HAS_CXX20 vvv
|
||||
_Dest[_Ix] = _Func(_Src[_Ix], _Src[_Ix - 1]);
|
||||
#endif // ^^^ !_HAS_CXX20 ^^^
|
||||
}
|
||||
}
|
||||
|
||||
_EXPORT_STD template <class _InIt, class _OutIt, class _BinOp>
|
||||
_CONSTEXPR20 _OutIt adjacent_difference(const _InIt _First, const _InIt _Last, _OutIt _Dest, _BinOp _Func) {
|
||||
// compute adjacent differences into _Dest
|
||||
|
@ -469,6 +483,32 @@ _CONSTEXPR20 _OutIt adjacent_difference(const _InIt _First, const _InIt _Last, _
|
|||
const auto _ULast = _STD _Get_unwrapped(_Last);
|
||||
auto _UDest = _STD _Get_unwrapped_n(_Dest, _STD _Idl_distance<_InIt>(_UFirst, _ULast));
|
||||
if (_UFirst != _ULast) {
|
||||
if constexpr (_Iterators_are_contiguous<decltype(_UFirst), decltype(_UDest)> && !_Iterator_is_volatile<_InIt>
|
||||
&& is_trivially_copyable_v<_Iter_value_t<_InIt>>) {
|
||||
#if _HAS_CXX20
|
||||
if (!_STD is_constant_evaluated())
|
||||
#endif
|
||||
{
|
||||
// Go with pointers and without loop-carried dependency to enable vectorization
|
||||
const auto _First_ptr = _STD _To_address(_UFirst);
|
||||
const auto _Last_ptr = _STD _To_address(_ULast);
|
||||
const auto _Dest_ptr = _STD _To_address(_UDest);
|
||||
const auto _Count = _Last_ptr - _First_ptr;
|
||||
|
||||
// Need to perform aliasing analysis.
|
||||
// The vectorizer is generally able to do that on its own, and would guard the vectorized code with
|
||||
// that, but when we eliminate the loop-carried dependency we change the semantics of the unvectorized
|
||||
// code too. So we need to perform this check manually, and after that we can tell the compiler that
|
||||
// there's no aliasing, to avoid it checking for that again.
|
||||
if (reinterpret_cast<uintptr_t>(_Dest_ptr + _Count) <= reinterpret_cast<uintptr_t>(_First_ptr)
|
||||
|| reinterpret_cast<uintptr_t>(_Last_ptr) <= reinterpret_cast<uintptr_t>(_Dest_ptr)) {
|
||||
_STD _Adjacent_difference_no_overlap(_Dest_ptr, _First_ptr, _Count, _STD _Pass_fn(_Func));
|
||||
_STD _Seek_wrapped(_Dest, _UDest + _Count);
|
||||
return _Dest;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
_Iter_value_t<_InIt> _Val(*_UFirst);
|
||||
*_UDest = _Val;
|
||||
while (++_UFirst != _ULast) { // compute another difference
|
||||
|
|
|
@ -605,18 +605,6 @@ bool _Is_word(_Elem _Ch) {
|
|||
return _UCh <= static_cast<_UElem>('z') && _Is_word(static_cast<unsigned char>(_UCh));
|
||||
}
|
||||
|
||||
#if _HAS_CXX20
|
||||
template <class _Ty, class = void>
|
||||
struct _Get_member_comparison_category {
|
||||
using type = weak_ordering;
|
||||
};
|
||||
|
||||
template <class _Ty>
|
||||
struct _Get_member_comparison_category<_Ty, void_t<typename _Ty::comparison_category>> {
|
||||
using type = _Ty::comparison_category;
|
||||
};
|
||||
#endif // _HAS_CXX20
|
||||
|
||||
_EXPORT_STD template <class _BidIt>
|
||||
class sub_match : public pair<_BidIt, _BidIt> { // class to hold contents of a capture group
|
||||
public:
|
||||
|
@ -630,10 +618,6 @@ public:
|
|||
// Note that _Size_type should always be std::size_t
|
||||
using _Size_type = typename string_type::size_type;
|
||||
|
||||
#if _HAS_CXX20
|
||||
using _Comparison_category = _Get_member_comparison_category<_Traits>::type;
|
||||
#endif // _HAS_CXX20
|
||||
|
||||
constexpr sub_match() : _Mybase(), matched(false) {}
|
||||
|
||||
bool matched;
|
||||
|
@ -744,7 +728,8 @@ _NODISCARD bool operator==(const sub_match<_BidIt>& _Left, const sub_match<_BidI
|
|||
#if _HAS_CXX20
|
||||
_EXPORT_STD template <class _BidIt>
|
||||
_NODISCARD auto operator<=>(const sub_match<_BidIt>& _Left, const sub_match<_BidIt>& _Right) {
|
||||
return static_cast<sub_match<_BidIt>::_Comparison_category>(_Left.compare(_Right) <=> 0);
|
||||
using _Comparison_category = _Get_comparison_category_t<char_traits<_Iter_value_t<_BidIt>>>;
|
||||
return static_cast<_Comparison_category>(_Left.compare(_Right) <=> 0);
|
||||
}
|
||||
#else // ^^^ _HAS_CXX20 / !_HAS_CXX20 vvv
|
||||
template <class _BidIt>
|
||||
|
@ -781,7 +766,8 @@ _NODISCARD bool operator==(const sub_match<_BidIt>& _Left, const _Iter_value_t<_
|
|||
#if _HAS_CXX20
|
||||
_EXPORT_STD template <class _BidIt>
|
||||
_NODISCARD auto operator<=>(const sub_match<_BidIt>& _Left, const _Iter_value_t<_BidIt>* _Right) {
|
||||
return static_cast<sub_match<_BidIt>::_Comparison_category>(_Left.compare(_Right) <=> 0);
|
||||
using _Comparison_category = _Get_comparison_category_t<char_traits<_Iter_value_t<_BidIt>>>;
|
||||
return static_cast<_Comparison_category>(_Left.compare(_Right) <=> 0);
|
||||
}
|
||||
#else // ^^^ _HAS_CXX20 / !_HAS_CXX20 vvv
|
||||
template <class _BidIt>
|
||||
|
@ -848,7 +834,8 @@ _NODISCARD bool operator==(const sub_match<_BidIt>& _Left, const _Iter_value_t<_
|
|||
#if _HAS_CXX20
|
||||
_EXPORT_STD template <class _BidIt>
|
||||
_NODISCARD auto operator<=>(const sub_match<_BidIt>& _Left, const _Iter_value_t<_BidIt>& _Right) {
|
||||
return static_cast<sub_match<_BidIt>::_Comparison_category>(_Left._Compare(_STD addressof(_Right), 1) <=> 0);
|
||||
using _Comparison_category = _Get_comparison_category_t<char_traits<_Iter_value_t<_BidIt>>>;
|
||||
return static_cast<_Comparison_category>(_Left._Compare(_STD addressof(_Right), 1) <=> 0);
|
||||
}
|
||||
#else // ^^^ _HAS_CXX20 / !_HAS_CXX20 vvv
|
||||
template <class _BidIt>
|
||||
|
@ -917,7 +904,8 @@ _NODISCARD bool operator==(
|
|||
_EXPORT_STD template <class _BidIt, class _Traits, class _Alloc>
|
||||
_NODISCARD auto operator<=>(
|
||||
const sub_match<_BidIt>& _Left, const basic_string<_Iter_value_t<_BidIt>, _Traits, _Alloc>& _Right) {
|
||||
return static_cast<sub_match<_BidIt>::_Comparison_category>(_Left._Compare(_Right.data(), _Right.size()) <=> 0);
|
||||
using _Comparison_category = _Get_comparison_category_t<char_traits<_Iter_value_t<_BidIt>>>;
|
||||
return static_cast<_Comparison_category>(_Left._Compare(_Right.data(), _Right.size()) <=> 0);
|
||||
}
|
||||
#else // ^^^ _HAS_CXX20 / !_HAS_CXX20 vvv
|
||||
template <class _BidIt, class _Traits, class _Alloc>
|
||||
|
|
|
@ -590,7 +590,7 @@ public:
|
|||
template <class... _Valtys>
|
||||
conditional_t<_Multi, iterator, pair<iterator, bool>> emplace(_Valtys&&... _Vals) {
|
||||
// try to insert value_type(_Vals...)
|
||||
using _In_place_key_extractor = typename _Traits::template _In_place_key_extractor<_Remove_cvref_t<_Valtys>...>;
|
||||
using _In_place_key_extractor = typename _Traits::template _In_place_key_extractor<_Valtys...>;
|
||||
if constexpr (_Multi) {
|
||||
_Check_max_size();
|
||||
_List_node_emplace_op2<_Alnode> _Newnode(_List._Getal(), _STD forward<_Valtys>(_Vals)...);
|
||||
|
@ -642,7 +642,7 @@ public:
|
|||
|
||||
template <class... _Valtys>
|
||||
iterator emplace_hint(const_iterator _Hint, _Valtys&&... _Vals) { // try to insert value_type(_Vals...)
|
||||
using _In_place_key_extractor = typename _Traits::template _In_place_key_extractor<_Remove_cvref_t<_Valtys>...>;
|
||||
using _In_place_key_extractor = typename _Traits::template _In_place_key_extractor<_Valtys...>;
|
||||
if constexpr (_Multi) {
|
||||
_Check_max_size();
|
||||
_List_node_emplace_op2<_Alnode> _Newnode(_List._Getal(), _STD forward<_Valtys>(_Vals)...);
|
||||
|
|
|
@ -2088,15 +2088,22 @@ _CXX17_DEPRECATE_TEMPORARY_BUFFER void return_temporary_buffer(_Ty* _Pbuf) {
|
|||
}
|
||||
#endif // _HAS_DEPRECATED_TEMPORARY_BUFFER
|
||||
|
||||
// assumes _Args have already been _Remove_cvref_t'd
|
||||
// The key_type of an (unordered) associative container is cv-unqualified, and we can't bind const Key& to a
|
||||
// volatile glvalue. Also, Cpp17CopyInsertable and Cpp17MoveInsertable don't require value-preservation for
|
||||
// the construction from a volatile glvalue, so generally we can't perform this optimization for them.
|
||||
// See N4993 [container.alloc.reqmts]/2.3, /2.4.
|
||||
template <class _Ty>
|
||||
using _Remove_const_ref_t = remove_const_t<remove_reference_t<_Ty>>;
|
||||
|
||||
// assumes _Args have already been _Remove_const_ref_t'd
|
||||
template <class _Key, class... _Args>
|
||||
struct _In_place_key_extract_set {
|
||||
struct _In_place_key_extract_set_impl {
|
||||
// by default we can't extract the key in the emplace family and must construct a node we might not use
|
||||
static constexpr bool _Extractable = false;
|
||||
};
|
||||
|
||||
template <class _Key>
|
||||
struct _In_place_key_extract_set<_Key, _Key> {
|
||||
struct _In_place_key_extract_set_impl<_Key, _Key> {
|
||||
// we can extract the key in emplace if the emplaced type is identical to the key type
|
||||
static constexpr bool _Extractable = true;
|
||||
static const _Key& _Extract(const _Key& _Val) noexcept {
|
||||
|
@ -2104,15 +2111,18 @@ struct _In_place_key_extract_set<_Key, _Key> {
|
|||
}
|
||||
};
|
||||
|
||||
// assumes _Args have already been _Remove_cvref_t'd
|
||||
template <class... _Valtys>
|
||||
using _In_place_key_extract_set = _In_place_key_extract_set_impl<_Remove_const_ref_t<_Valtys>...>;
|
||||
|
||||
// assumes _Args have already been _Remove_const_ref_t'd
|
||||
template <class _Key, class... _Args>
|
||||
struct _In_place_key_extract_map {
|
||||
struct _In_place_key_extract_map_impl {
|
||||
// by default we can't extract the key in the emplace family and must construct a node we might not use
|
||||
static constexpr bool _Extractable = false;
|
||||
};
|
||||
|
||||
template <class _Key, class _Second>
|
||||
struct _In_place_key_extract_map<_Key, _Key, _Second> {
|
||||
struct _In_place_key_extract_map_impl<_Key, _Key, _Second> {
|
||||
// if we would call the pair(key, value) constructor family, we can use the first parameter as the key
|
||||
static constexpr bool _Extractable = true;
|
||||
static const _Key& _Extract(const _Key& _Val, const _Second&) noexcept {
|
||||
|
@ -2121,14 +2131,49 @@ struct _In_place_key_extract_map<_Key, _Key, _Second> {
|
|||
};
|
||||
|
||||
template <class _Key, class _First, class _Second>
|
||||
struct _In_place_key_extract_map<_Key, pair<_First, _Second>> {
|
||||
struct _In_place_key_extract_map_impl<_Key, pair<_First, _Second>> {
|
||||
// if we would call the pair(pair<other, other>) constructor family, we can use the pair.first member as the key
|
||||
static constexpr bool _Extractable = is_same_v<_Key, _Remove_cvref_t<_First>>;
|
||||
static const _Key& _Extract(const pair<_First, _Second>& _Val) {
|
||||
static constexpr bool _Extractable = is_same_v<_Key, _Remove_const_ref_t<_First>>;
|
||||
static const _Key& _Extract(const pair<_First, _Second>& _Val) noexcept {
|
||||
return _Val.first;
|
||||
}
|
||||
};
|
||||
|
||||
#if _HAS_CXX23
|
||||
// if we would call the pair(pair-like) constructor family and the argument is not a subrange,
|
||||
// we can use get<0>(pair-like) as the key
|
||||
|
||||
template <class _Key, class _Elem>
|
||||
struct _In_place_key_extract_map_impl<_Key, array<_Elem, 2>> {
|
||||
static constexpr bool _Extractable = is_same_v<_Key, remove_const_t<_Elem>>;
|
||||
static const _Key& _Extract(const array<_Elem, 2>& _Val) noexcept {
|
||||
return _Val[0];
|
||||
}
|
||||
};
|
||||
|
||||
template <class _Key, class _First, class _Second>
|
||||
struct _In_place_key_extract_map_impl<_Key, tuple<_First, _Second>> {
|
||||
static constexpr bool _Extractable = is_same_v<_Key, _Remove_const_ref_t<_First>>;
|
||||
static const _Key& _Extract(const tuple<_First, _Second>& _Val) noexcept {
|
||||
return _STD get<0>(_Val);
|
||||
}
|
||||
};
|
||||
#endif // _HAS_CXX23
|
||||
|
||||
template <class _Key, class _First, class... _RestTypes>
|
||||
struct _In_place_key_extract_map_impl<_Key, piecewise_construct_t, tuple<_First>, tuple<_RestTypes...>> {
|
||||
// if we would call the piecewise_construct_t constructor and the first argument is a 1-tuple,
|
||||
// we can use get<0>(first_tuple) as the key
|
||||
static constexpr bool _Extractable = is_same_v<_Key, _Remove_const_ref_t<_First>>;
|
||||
static const _Key& _Extract(
|
||||
const piecewise_construct_t&, const tuple<_First>& _Tup_val, const tuple<_RestTypes...>&) noexcept {
|
||||
return _STD get<0>(_Tup_val);
|
||||
}
|
||||
};
|
||||
|
||||
template <class... _Valtys>
|
||||
using _In_place_key_extract_map = _In_place_key_extract_map_impl<_Remove_const_ref_t<_Valtys>...>;
|
||||
|
||||
#pragma warning(push)
|
||||
#pragma warning(disable : 4624) // '%s': destructor was implicitly defined as deleted
|
||||
template <class _Ty>
|
||||
|
|
|
@ -998,7 +998,7 @@ private:
|
|||
protected:
|
||||
template <class... _Valtys>
|
||||
pair<_Nodeptr, bool> _Emplace(_Valtys&&... _Vals) {
|
||||
using _In_place_key_extractor = typename _Traits::template _In_place_key_extractor<_Remove_cvref_t<_Valtys>...>;
|
||||
using _In_place_key_extractor = typename _Traits::template _In_place_key_extractor<_Valtys...>;
|
||||
const auto _Scary = _Get_scary();
|
||||
_Tree_find_result<_Nodeptr> _Loc;
|
||||
_Nodeptr _Inserted;
|
||||
|
@ -1042,7 +1042,7 @@ public:
|
|||
protected:
|
||||
template <class... _Valtys>
|
||||
_Nodeptr _Emplace_hint(const _Nodeptr _Hint, _Valtys&&... _Vals) {
|
||||
using _In_place_key_extractor = typename _Traits::template _In_place_key_extractor<_Remove_cvref_t<_Valtys>...>;
|
||||
using _In_place_key_extractor = typename _Traits::template _In_place_key_extractor<_Valtys...>;
|
||||
const auto _Scary = _Get_scary();
|
||||
_Tree_find_hint_result<_Nodeptr> _Loc;
|
||||
_Nodeptr _Inserted;
|
||||
|
|
|
@ -3633,6 +3633,252 @@ namespace {
|
|||
return _Last1;
|
||||
}
|
||||
}
|
||||
|
||||
template <class _Traits, class _Ty>
|
||||
const void* __stdcall __std_find_end_impl(
|
||||
const void* const _First1, const void* const _Last1, const void* const _First2, const size_t _Count2) noexcept {
|
||||
if (_Count2 == 0) {
|
||||
return _Last1;
|
||||
}
|
||||
|
||||
if (_Count2 == 1) {
|
||||
return __std_find_last_trivial_impl<_Traits>(_First1, _Last1, *static_cast<const _Ty*>(_First2));
|
||||
}
|
||||
|
||||
const size_t _Size_bytes_1 = _Byte_length(_First1, _Last1);
|
||||
const size_t _Size_bytes_2 = _Count2 * sizeof(_Ty);
|
||||
|
||||
if (_Size_bytes_1 < _Size_bytes_2) {
|
||||
return _Last1;
|
||||
}
|
||||
|
||||
#ifndef _M_ARM64EC
|
||||
if (_Use_sse42() && _Size_bytes_1 >= 16) {
|
||||
constexpr int _Op = (sizeof(_Ty) == 1 ? _SIDD_UBYTE_OPS : _SIDD_UWORD_OPS) | _SIDD_CMP_EQUAL_ORDERED;
|
||||
constexpr int _Part_size_el = sizeof(_Ty) == 1 ? 16 : 8;
|
||||
|
||||
static constexpr int8_t _Low_part_mask[] = {//
|
||||
-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, //
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0};
|
||||
|
||||
if (_Size_bytes_2 <= 16) {
|
||||
const int _Size_el_2 = static_cast<int>(_Count2);
|
||||
constexpr unsigned int _Whole_mask = (1 << _Part_size_el) - 1;
|
||||
const unsigned int _Needle_fit_mask = (1 << (_Part_size_el - _Size_el_2 + 1)) - 1;
|
||||
const unsigned int _Needle_unfit_mask = _Whole_mask ^ _Needle_fit_mask;
|
||||
|
||||
const void* _Stop1 = _First1;
|
||||
_Advance_bytes(_Stop1, _Size_bytes_1 & 0xF);
|
||||
|
||||
alignas(16) uint8_t _Tmp2[16];
|
||||
memcpy(_Tmp2, _First2, _Size_bytes_2);
|
||||
const __m128i _Data2 = _mm_load_si128(reinterpret_cast<const __m128i*>(_Tmp2));
|
||||
|
||||
const void* _Mid1 = _Last1;
|
||||
_Rewind_bytes(_Mid1, 16);
|
||||
|
||||
const auto _Check_fit = [&_Mid1, _Needle_fit_mask](const unsigned int _Match) noexcept {
|
||||
const unsigned int _Fit_match = _Match & _Needle_fit_mask;
|
||||
if (_Fit_match != 0) {
|
||||
unsigned long _Match_last_pos;
|
||||
|
||||
// CodeQL [SM02313] Result is always initialized: we just tested that _Fit_match is non-zero.
|
||||
_BitScanReverse(&_Match_last_pos, _Fit_match);
|
||||
|
||||
_Advance_bytes(_Mid1, _Match_last_pos * sizeof(_Ty));
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
};
|
||||
|
||||
#pragma warning(push)
|
||||
#pragma warning(disable : 4324) // structure was padded due to alignment specifier
|
||||
const auto _Check_unfit = [=, &_Mid1](const unsigned int _Match) noexcept {
|
||||
long _Unfit_match = _Match & _Needle_unfit_mask;
|
||||
while (_Unfit_match != 0) {
|
||||
const void* _Tmp1 = _Mid1;
|
||||
unsigned long _Match_last_pos;
|
||||
|
||||
// CodeQL [SM02313] Result is always initialized: we just tested that _Unfit_match is non-zero.
|
||||
_BitScanReverse(&_Match_last_pos, _Unfit_match);
|
||||
|
||||
_Advance_bytes(_Tmp1, _Match_last_pos * sizeof(_Ty));
|
||||
|
||||
const __m128i _Match_data = _mm_loadu_si128(reinterpret_cast<const __m128i*>(_Tmp1));
|
||||
const __m128i _Cmp_result = _mm_xor_si128(_Data2, _Match_data);
|
||||
const __m128i _Data_mask =
|
||||
_mm_loadu_si128(reinterpret_cast<const __m128i*>(_Low_part_mask + 16 - _Size_bytes_2));
|
||||
|
||||
if (_mm_testz_si128(_Cmp_result, _Data_mask)) {
|
||||
_Mid1 = _Tmp1;
|
||||
return true;
|
||||
}
|
||||
|
||||
_bittestandreset(&_Unfit_match, _Match_last_pos);
|
||||
}
|
||||
|
||||
return false;
|
||||
};
|
||||
#pragma warning(pop)
|
||||
|
||||
// TRANSITION, DevCom-10689455, the code below could test with _mm_cmpestrc,
|
||||
// if it has been fused with _mm_cmpestrm.
|
||||
|
||||
// The very last part, for any match needle should fit, otherwise false match
|
||||
const __m128i _Data1_last = _mm_loadu_si128(reinterpret_cast<const __m128i*>(_Mid1));
|
||||
const auto _Match_last = _mm_cmpestrm(_Data2, _Size_el_2, _Data1_last, _Part_size_el, _Op);
|
||||
const unsigned int _Match_last_val = _mm_cvtsi128_si32(_Match_last);
|
||||
if (_Check_fit(_Match_last_val)) {
|
||||
return _Mid1;
|
||||
}
|
||||
|
||||
// The middle part, fit and unfit needle
|
||||
while (_Mid1 != _Stop1) {
|
||||
_Rewind_bytes(_Mid1, 16);
|
||||
const __m128i _Data1 = _mm_loadu_si128(reinterpret_cast<const __m128i*>(_Mid1));
|
||||
const auto _Match = _mm_cmpestrm(_Data2, _Size_el_2, _Data1, _Part_size_el, _Op);
|
||||
const unsigned int _Match_val = _mm_cvtsi128_si32(_Match);
|
||||
if (_Match_val != 0 && (_Check_unfit(_Match_val) || _Check_fit(_Match_val))) {
|
||||
return _Mid1;
|
||||
}
|
||||
}
|
||||
|
||||
// The first part, fit and unfit needle, mask out already processed positions
|
||||
if (const size_t _Tail_bytes_1 = _Size_bytes_1 & 0xF; _Tail_bytes_1 != 0) {
|
||||
_Mid1 = _First1;
|
||||
const __m128i _Data1 = _mm_loadu_si128(reinterpret_cast<const __m128i*>(_Mid1));
|
||||
const auto _Match = _mm_cmpestrm(_Data2, _Size_el_2, _Data1, _Part_size_el, _Op);
|
||||
const unsigned int _Match_val = _mm_cvtsi128_si32(_Match) & ((1 << _Tail_bytes_1) - 1);
|
||||
if (_Match_val != 0 && (_Check_unfit(_Match_val) || _Check_fit(_Match_val))) {
|
||||
return _Mid1;
|
||||
}
|
||||
}
|
||||
|
||||
return _Last1;
|
||||
} else {
|
||||
const __m128i _Data2 = _mm_loadu_si128(reinterpret_cast<const __m128i*>(_First2));
|
||||
|
||||
const void* _Tail2 = _First2;
|
||||
_Advance_bytes(_Tail2, 16);
|
||||
|
||||
const void* _Mid1 = _Last1;
|
||||
_Rewind_bytes(_Mid1, _Size_bytes_2);
|
||||
|
||||
const size_t _Size_diff_bytes = _Size_bytes_1 - _Size_bytes_2;
|
||||
const void* _Stop1 = _First1;
|
||||
_Advance_bytes(_Stop1, _Size_diff_bytes & 0xF);
|
||||
|
||||
#pragma warning(push)
|
||||
#pragma warning(disable : 4324) // structure was padded due to alignment specifier
|
||||
const auto _Check = [=, &_Mid1](long _Match) noexcept {
|
||||
while (_Match != 0) {
|
||||
const void* _Tmp1 = _Mid1;
|
||||
unsigned long _Match_last_pos;
|
||||
|
||||
// CodeQL [SM02313] Result is always initialized: we just tested that _Match is non-zero.
|
||||
_BitScanReverse(&_Match_last_pos, _Match);
|
||||
|
||||
bool _Match_1st_16 = true;
|
||||
|
||||
if (_Match_last_pos != 0) {
|
||||
_Advance_bytes(_Tmp1, _Match_last_pos * sizeof(_Ty));
|
||||
|
||||
const __m128i _Match_data = _mm_loadu_si128(reinterpret_cast<const __m128i*>(_Tmp1));
|
||||
const __m128i _Cmp_result = _mm_xor_si128(_Data2, _Match_data);
|
||||
|
||||
if (!_mm_testz_si128(_Cmp_result, _Cmp_result)) {
|
||||
_Match_1st_16 = false;
|
||||
}
|
||||
}
|
||||
|
||||
if (_Match_1st_16) {
|
||||
const void* _Tail1 = _Tmp1;
|
||||
_Advance_bytes(_Tail1, 16);
|
||||
|
||||
if (memcmp(_Tail1, _Tail2, _Size_bytes_2 - 16) == 0) {
|
||||
_Mid1 = _Tmp1;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
_bittestandreset(&_Match, _Match_last_pos);
|
||||
}
|
||||
|
||||
return false;
|
||||
};
|
||||
#pragma warning(pop)
|
||||
// The very last part, just compare, as true match must start with first symbol
|
||||
const __m128i _Data1_last = _mm_loadu_si128(reinterpret_cast<const __m128i*>(_Mid1));
|
||||
const __m128i _Match_last = _mm_xor_si128(_Data2, _Data1_last);
|
||||
if (_mm_testz_si128(_Match_last, _Match_last)) {
|
||||
// Matched 16 bytes, check the rest
|
||||
const void* _Tail1 = _Mid1;
|
||||
_Advance_bytes(_Tail1, 16);
|
||||
|
||||
if (memcmp(_Tail1, _Tail2, _Size_bytes_2 - 16) == 0) {
|
||||
return _Mid1;
|
||||
}
|
||||
}
|
||||
|
||||
// TRANSITION, DevCom-10689455, the code below could test with _mm_cmpestrc,
|
||||
// if it has been fused with _mm_cmpestrm.
|
||||
|
||||
// The main part, match all characters
|
||||
while (_Mid1 != _Stop1) {
|
||||
_Rewind_bytes(_Mid1, 16);
|
||||
|
||||
const __m128i _Data1 = _mm_loadu_si128(reinterpret_cast<const __m128i*>(_Mid1));
|
||||
const auto _Match = _mm_cmpestrm(_Data2, _Part_size_el, _Data1, _Part_size_el, _Op);
|
||||
const unsigned int _Match_val = _mm_cvtsi128_si32(_Match);
|
||||
if (_Match_val != 0 && _Check(_Match_val)) {
|
||||
return _Mid1;
|
||||
}
|
||||
}
|
||||
|
||||
// The first part, mask out already processed positions
|
||||
if (const size_t _Tail_bytes_1 = _Size_diff_bytes & 0xF; _Tail_bytes_1 != 0) {
|
||||
_Mid1 = _First1;
|
||||
const __m128i _Data1 = _mm_loadu_si128(reinterpret_cast<const __m128i*>(_Mid1));
|
||||
const auto _Match = _mm_cmpestrm(_Data2, _Part_size_el, _Data1, _Part_size_el, _Op);
|
||||
const unsigned int _Match_val = _mm_cvtsi128_si32(_Match) & ((1 << _Tail_bytes_1) - 1);
|
||||
if (_Match_val != 0 && _Check(_Match_val)) {
|
||||
return _Mid1;
|
||||
}
|
||||
}
|
||||
|
||||
return _Last1;
|
||||
}
|
||||
} else
|
||||
#endif // !defined(_M_ARM64EC)
|
||||
{
|
||||
auto _Ptr1 = static_cast<const _Ty*>(_Last1) - _Count2;
|
||||
const auto _Ptr2 = static_cast<const _Ty*>(_First2);
|
||||
|
||||
for (;;) {
|
||||
if (*_Ptr1 == *_Ptr2) {
|
||||
bool _Equal = true;
|
||||
|
||||
for (size_t _Idx = 1; _Idx != _Count2; ++_Idx) {
|
||||
if (_Ptr1[_Idx] != _Ptr2[_Idx]) {
|
||||
_Equal = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (_Equal) {
|
||||
return _Ptr1;
|
||||
}
|
||||
}
|
||||
|
||||
if (_Ptr1 == _First1) {
|
||||
return _Last1;
|
||||
}
|
||||
|
||||
--_Ptr1;
|
||||
}
|
||||
}
|
||||
}
|
||||
} // unnamed namespace
|
||||
|
||||
extern "C" {
|
||||
|
@ -3757,6 +4003,16 @@ const void* __stdcall __std_search_2(
|
|||
return __std_search_impl<_Find_traits_2, uint16_t>(_First1, _Last1, _First2, _Count2);
|
||||
}
|
||||
|
||||
const void* __stdcall __std_find_end_1(
|
||||
const void* const _First1, const void* const _Last1, const void* const _First2, const size_t _Count2) noexcept {
|
||||
return __std_find_end_impl<_Find_traits_1, uint8_t>(_First1, _Last1, _First2, _Count2);
|
||||
}
|
||||
|
||||
const void* __stdcall __std_find_end_2(
|
||||
const void* const _First1, const void* const _Last1, const void* const _First2, const size_t _Count2) noexcept {
|
||||
return __std_find_end_impl<_Find_traits_2, uint16_t>(_First1, _Last1, _First2, _Count2);
|
||||
}
|
||||
|
||||
__declspec(noalias) size_t __stdcall __std_mismatch_1(
|
||||
const void* const _First1, const void* const _First2, const size_t _Count) noexcept {
|
||||
return __std_mismatch_impl<_Find_traits_1, uint8_t>(_First1, _First2, _Count);
|
||||
|
|
|
@ -24,10 +24,6 @@ std/time/time.syn/formatter.year_month_weekday.pass.cpp:1 FAIL
|
|||
std/time/time.syn/formatter.zoned_time.pass.cpp:0 FAIL
|
||||
std/time/time.syn/formatter.zoned_time.pass.cpp:1 FAIL
|
||||
|
||||
# LLVM-74756: [libc++][test] overload_compare_iterator doesn't support its claimed iterator_category
|
||||
std/utilities/memory/specialized.algorithms/uninitialized.copy/uninitialized_copy.pass.cpp FAIL
|
||||
std/utilities/memory/specialized.algorithms/uninitialized.move/uninitialized_move.pass.cpp FAIL
|
||||
|
||||
# LLVM-90196: [libc++][format] Formatting range with m range-type is incorrect
|
||||
std/utilities/format/format.range/format.range.formatter/format.functions.format.pass.cpp FAIL
|
||||
std/utilities/format/format.range/format.range.formatter/format.functions.vformat.pass.cpp FAIL
|
||||
|
@ -37,12 +33,8 @@ std/utilities/format/format.range/format.range.fmtset/format.functions.vformat.p
|
|||
# LLVM-100506: [libc++][test] Precondition violation in rand.dist.uni.real/param_ctor.pass.cpp
|
||||
std/numerics/rand/rand.dist/rand.dist.uni/rand.dist.uni.real/param_ctor.pass.cpp FAIL
|
||||
|
||||
# LLVM-105878: [libc++][test] fp_compare.h includes non-portable <__config>
|
||||
std/numerics/c.math/cmath.pass.cpp FAIL
|
||||
std/numerics/numeric.ops/numeric.ops.midpoint/midpoint.float.pass.cpp FAIL
|
||||
|
||||
# LLVM-105966: [libc++][test] Fix is_always_lock_free test
|
||||
std/atomics/atomics.lockfree/is_always_lock_free.cpp FAIL
|
||||
# LLVM-113609: [libc++][test] Non-rebindable test_alloc in string.capacity/deallocate_size.pass.cpp
|
||||
std/strings/basic.string/string.capacity/deallocate_size.pass.cpp FAIL
|
||||
|
||||
# Non-Standard regex behavior.
|
||||
# "It seems likely that the test is still non-conforming due to how libc++ handles the 'w' character class."
|
||||
|
@ -80,10 +72,9 @@ std/numerics/rand/rand.util/rand.util.canonical/generate_canonical.pass.cpp FAIL
|
|||
# Test expects __cpp_lib_chrono to have the old value 201611L for P0505R0; we define the C++20 value 201907L for P1466R3.
|
||||
std/language.support/support.limits/support.limits.general/chrono.version.compile.pass.cpp FAIL
|
||||
|
||||
# Tests expect __cpp_lib_ranges to have the old value 201811L for P0896R4; we define the C++20 value 201911L for P1716R3.
|
||||
# Test expects __cpp_lib_freestanding_algorithm to not be defined before C++26; we define it unconditionally.
|
||||
# Test expects __cpp_lib_shift to have the C++20 value 201806L for P0769R2; we define the C++23 value 202202L for P2440R1.
|
||||
std/language.support/support.limits/support.limits.general/algorithm.version.compile.pass.cpp FAIL
|
||||
std/language.support/support.limits/support.limits.general/functional.version.compile.pass.cpp FAIL
|
||||
std/language.support/support.limits/support.limits.general/iterator.version.compile.pass.cpp FAIL
|
||||
|
||||
# Test expects __cpp_lib_print to have the old value 202207L for P2093R14; we define the C++23 value 202406L for P3235R3.
|
||||
std/language.support/support.limits/support.limits.general/ostream.version.compile.pass.cpp FAIL
|
||||
|
@ -157,19 +148,6 @@ std/language.support/support.limits/support.limits.general/format.version.compil
|
|||
# libc++ doesn't implement LWG-3670
|
||||
std/ranges/range.factories/range.iota.view/iterator/member_typedefs.compile.pass.cpp FAIL
|
||||
|
||||
# libc++ doesn't implement LWG-3870
|
||||
std/utilities/memory/specialized.algorithms/specialized.construct/ranges_construct_at.pass.cpp FAIL
|
||||
std/utilities/memory/specialized.algorithms/uninitialized.construct.default/ranges_uninitialized_default_construct.pass.cpp FAIL
|
||||
std/utilities/memory/specialized.algorithms/uninitialized.construct.default/ranges_uninitialized_default_construct_n.pass.cpp FAIL
|
||||
std/utilities/memory/specialized.algorithms/uninitialized.construct.value/ranges_uninitialized_value_construct.pass.cpp FAIL
|
||||
std/utilities/memory/specialized.algorithms/uninitialized.construct.value/ranges_uninitialized_value_construct_n.pass.cpp FAIL
|
||||
std/utilities/memory/specialized.algorithms/uninitialized.copy/ranges_uninitialized_copy.pass.cpp FAIL
|
||||
std/utilities/memory/specialized.algorithms/uninitialized.copy/ranges_uninitialized_copy_n.pass.cpp FAIL
|
||||
std/utilities/memory/specialized.algorithms/uninitialized.fill/ranges_uninitialized_fill.pass.cpp FAIL
|
||||
std/utilities/memory/specialized.algorithms/uninitialized.fill.n/ranges_uninitialized_fill_n.pass.cpp FAIL
|
||||
std/utilities/memory/specialized.algorithms/uninitialized.move/ranges_uninitialized_move.pass.cpp FAIL
|
||||
std/utilities/memory/specialized.algorithms/uninitialized.move/ranges_uninitialized_move_n.pass.cpp FAIL
|
||||
|
||||
# libc++ doesn't implement LWG-4013
|
||||
std/ranges/range.adaptors/range.lazy.split/range.lazy.split.outer.value/ctor.default.pass.cpp FAIL
|
||||
std/ranges/range.adaptors/range.lazy.split/range.lazy.split.outer.value/ctor.iter.pass.cpp FAIL
|
||||
|
@ -237,6 +215,9 @@ std/language.support/support.limits/support.limits.general/cstdlib.version.compi
|
|||
# P2255R2 "Type Traits To Detect References Binding To Temporaries"
|
||||
std/language.support/support.limits/support.limits.general/type_traits.version.compile.pass.cpp FAIL
|
||||
|
||||
# P2674R1 is_implicit_lifetime
|
||||
std/utilities/meta/meta.unary/meta.unary.prop/is_implicit_lifetime.pass.cpp FAIL
|
||||
|
||||
|
||||
# *** MISSING COMPILER FEATURES ***
|
||||
# P1169R4 static operator()
|
||||
|
@ -247,15 +228,8 @@ std/thread/futures/futures.task/futures.task.members/ctad.static.compile.pass.cp
|
|||
std/utilities/function.objects/func.wrap/func.wrap.func/func.wrap.func.con/ctad.static.compile.pass.cpp:0 FAIL
|
||||
std/utilities/function.objects/func.wrap/func.wrap.func/func.wrap.func.con/ctad.static.compile.pass.cpp:1 FAIL
|
||||
|
||||
# P2128R6 Multidimensional Subscript Operator
|
||||
std/containers/views/mdspan/mdspan/index_operator.pass.cpp:0 FAIL
|
||||
std/containers/views/mdspan/mdspan/index_operator.pass.cpp:1 FAIL
|
||||
|
||||
|
||||
# *** MISSING LWG ISSUE RESOLUTIONS ***
|
||||
# LWG-2192 "Validity and return type of std::abs(0u) is unclear" (resolution is missing in UCRT, DevCom-10331466)
|
||||
std/depr/depr.c.headers/stdlib_h.pass.cpp FAIL
|
||||
|
||||
# LWG-2503 "multiline option should be added to syntax_option_type"
|
||||
std/re/re.const/re.matchflag/match_multiline.pass.cpp FAIL
|
||||
std/re/re.const/re.matchflag/match_not_eol.pass.cpp FAIL
|
||||
|
@ -276,11 +250,6 @@ std/thread/thread.condition/notify_all_at_thread_exit_lwg3343.pass.cpp SKIPPED
|
|||
|
||||
|
||||
# *** C1XX COMPILER BUGS ***
|
||||
# DevCom-409222 VSO-752709 "Constructing rvalue reference from non-reference-related lvalue reference"
|
||||
# Reportedly fixed in VS 2019 16.10, test is still failing, need to investigate.
|
||||
std/utilities/meta/meta.unary/meta.unary.prop/is_constructible.pass.cpp:0 FAIL
|
||||
std/utilities/meta/meta.unary/meta.unary.prop/is_constructible.pass.cpp:1 FAIL
|
||||
|
||||
# VSO-1271673 "static analyzer doesn't know about short-circuiting"
|
||||
# Note: The :1 (ASan) configuration doesn't run static analysis.
|
||||
std/algorithms/alg.sorting/alg.sort/partial.sort/partial_sort.pass.cpp:0 FAIL
|
||||
|
@ -747,9 +716,6 @@ std/re/re.alg/re.alg.search/basic.locale.pass.cpp FAIL
|
|||
std/re/re.alg/re.alg.search/ecma.locale.pass.cpp FAIL
|
||||
std/re/re.alg/re.alg.search/extended.locale.pass.cpp FAIL
|
||||
|
||||
# Not analyzed. Error mentions allocator<const T>.
|
||||
std/utilities/memory/specialized.algorithms/specialized.construct/construct_at.pass.cpp FAIL
|
||||
|
||||
# Not analyzed. Seems to force a sign conversion error?
|
||||
std/iterators/iterator.primitives/iterator.operations/advance.pass.cpp SKIPPED
|
||||
|
||||
|
@ -832,6 +798,8 @@ std/containers/sequences/vector/vector.cons/construct_size_value.pass.cpp:0 FAIL
|
|||
std/containers/sequences/vector/vector.cons/construct_size_value.pass.cpp:1 FAIL
|
||||
std/containers/sequences/vector/vector.cons/construct_size_value_alloc.pass.cpp:0 FAIL
|
||||
std/containers/sequences/vector/vector.cons/construct_size_value_alloc.pass.cpp:1 FAIL
|
||||
std/containers/views/mdspan/mdspan/index_operator.pass.cpp:0 FAIL
|
||||
std/containers/views/mdspan/mdspan/index_operator.pass.cpp:1 FAIL
|
||||
|
||||
# Not analyzed. Looks like a test bug, assuming that hash<vector<bool>> is constexpr.
|
||||
std/containers/sequences/vector.bool/enabled_hash.pass.cpp FAIL
|
||||
|
@ -858,9 +826,6 @@ std/iterators/predef.iterators/move.iterators/move.iter.ops/move.iter.op.comp/op
|
|||
std/ranges/range.adaptors/range.lazy.split/ctor.copy_move.pass.cpp FAIL
|
||||
std/ranges/range.adaptors/range.lazy.split/range.lazy.split.inner/iter_swap.pass.cpp FAIL
|
||||
|
||||
# Not analyzed. Checking whether packaged_task is constructible from an allocator and a packaged_task of a different type.
|
||||
std/thread/futures/futures.task/futures.task.members/ctor2.compile.pass.cpp FAIL
|
||||
|
||||
# Not analyzed.
|
||||
# MSVC error C2131: expression did not evaluate to a constant
|
||||
# MSVC note: failure was caused by a read of an uninitialized symbol
|
||||
|
|
|
@ -760,7 +760,7 @@ void test_hash_monostate() {
|
|||
static_assert(std::is_copy_constructible<H>::value, "");
|
||||
}
|
||||
{
|
||||
test_hash_enabled_for_type<std::monostate>();
|
||||
test_hash_enabled<std::monostate>();
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -795,18 +795,18 @@ namespace hash {
|
|||
void test_hash_variant_enabled() {
|
||||
{
|
||||
#ifndef __EDG__ // TRANSITION, DevCom-10107834
|
||||
test_hash_enabled_for_type<std::variant<int> >();
|
||||
test_hash_enabled_for_type<std::variant<int*, long, double, const int> >();
|
||||
test_hash_enabled<std::variant<int> >();
|
||||
test_hash_enabled<std::variant<int*, long, double, const int> >();
|
||||
#endif // ^^^ no workaround ^^^
|
||||
}
|
||||
{
|
||||
test_hash_disabled_for_type<std::variant<int, A>>();
|
||||
test_hash_disabled_for_type<std::variant<const A, void*>>();
|
||||
test_hash_disabled<std::variant<int, A>>();
|
||||
test_hash_disabled<std::variant<const A, void*>>();
|
||||
}
|
||||
{
|
||||
#ifndef __EDG__ // TRANSITION, DevCom-10107834
|
||||
test_hash_enabled_for_type<std::variant<int, B>>();
|
||||
test_hash_enabled_for_type<std::variant<const B, int>>();
|
||||
test_hash_enabled<std::variant<int, B>>();
|
||||
test_hash_enabled<std::variant<const B, int>>();
|
||||
#endif // ^^^ no workaround ^^^
|
||||
}
|
||||
}
|
||||
|
|
|
@ -701,7 +701,7 @@ int run_test()
|
|||
struct TestConstexpr : public std::any {
|
||||
constexpr TestConstexpr() : std::any() {}
|
||||
};
|
||||
static TEST_CONSTINIT std::any a;
|
||||
TEST_CONSTINIT static std::any a;
|
||||
(void)a;
|
||||
}
|
||||
#endif // ^^^ no workaround ^^^
|
||||
|
|
|
@ -862,18 +862,18 @@ int run_test()
|
|||
}
|
||||
{
|
||||
#ifndef __EDG__ // TRANSITION, DevCom-10107834
|
||||
test_hash_enabled_for_type<std::optional<int> >();
|
||||
test_hash_enabled_for_type<std::optional<int*> >();
|
||||
test_hash_enabled_for_type<std::optional<const int> >();
|
||||
test_hash_enabled_for_type<std::optional<int* const> >();
|
||||
test_hash_enabled<std::optional<int> >();
|
||||
test_hash_enabled<std::optional<int*> >();
|
||||
test_hash_enabled<std::optional<const int> >();
|
||||
test_hash_enabled<std::optional<int* const> >();
|
||||
#endif // ^^^ no workaround ^^^
|
||||
|
||||
test_hash_disabled_for_type<std::optional<A>>();
|
||||
test_hash_disabled_for_type<std::optional<const A>>();
|
||||
test_hash_disabled<std::optional<A>>();
|
||||
test_hash_disabled<std::optional<const A>>();
|
||||
|
||||
#ifndef __EDG__ // TRANSITION, DevCom-10107834
|
||||
test_hash_enabled_for_type<std::optional<B>>();
|
||||
test_hash_enabled_for_type<std::optional<const B>>();
|
||||
test_hash_enabled<std::optional<B>>();
|
||||
test_hash_enabled<std::optional<const B>>();
|
||||
#endif // ^^^ no workaround ^^^
|
||||
}
|
||||
|
||||
|
|
|
@ -12,6 +12,7 @@
|
|||
#include <functional>
|
||||
#include <limits>
|
||||
#include <list>
|
||||
#include <numeric>
|
||||
#include <random>
|
||||
#include <stdexcept>
|
||||
#include <string>
|
||||
|
@ -34,6 +35,74 @@ using namespace std;
|
|||
#pragma clang diagnostic ignored "-Wc++17-extensions" // constexpr if is a C++17 extension
|
||||
#endif // __clang__
|
||||
|
||||
template <class InIt, class OutIt, class BinOp>
|
||||
OutIt last_known_good_adj_diff(InIt first, InIt last, OutIt dest, BinOp binop) {
|
||||
if (first == last) {
|
||||
return dest;
|
||||
}
|
||||
|
||||
auto val = *first;
|
||||
*dest = val;
|
||||
|
||||
for (++first, ++dest; first != last; ++first, ++dest) {
|
||||
auto tmp = *first;
|
||||
*dest = binop(tmp, val);
|
||||
val = tmp;
|
||||
}
|
||||
|
||||
return dest;
|
||||
}
|
||||
|
||||
template <class T>
|
||||
void test_case_adj_diff(const vector<T>& input, vector<T>& output_expected, vector<T>& output_actual) {
|
||||
// Avoid truncation warnings:
|
||||
const auto subtract = [](const T& left, const T& right) { return static_cast<T>(left - right); };
|
||||
const auto expected = last_known_good_adj_diff(input.begin(), input.end(), output_expected.begin(), subtract);
|
||||
const auto actual = adjacent_difference(input.begin(), input.end(), output_actual.begin(), subtract);
|
||||
|
||||
assert(actual - output_actual.begin() == expected - output_expected.begin());
|
||||
assert(output_actual == output_expected);
|
||||
}
|
||||
|
||||
template <class T>
|
||||
void test_adjacent_difference(mt19937_64& gen) {
|
||||
using Limits = numeric_limits<T>;
|
||||
|
||||
uniform_int_distribution<conditional_t<sizeof(T) == 1, int, T>> dis(
|
||||
is_signed_v<T> ? static_cast<T>(Limits::min() / 2) : Limits::min(),
|
||||
is_signed_v<T> ? static_cast<T>(Limits::max() / 2) : Limits::max());
|
||||
|
||||
vector<T> input;
|
||||
vector<T> output_expected;
|
||||
vector<T> output_actual;
|
||||
|
||||
for (const auto& v : {&input, &output_expected, &output_actual}) {
|
||||
v->reserve(dataCount);
|
||||
}
|
||||
|
||||
test_case_adj_diff(input, output_expected, output_actual);
|
||||
for (size_t attempts = 0; attempts < dataCount; ++attempts) {
|
||||
input.push_back(static_cast<T>(dis(gen)));
|
||||
|
||||
for (const auto& v : {&output_expected, &output_actual}) {
|
||||
v->assign(input.size(), 0);
|
||||
}
|
||||
|
||||
test_case_adj_diff(input, output_expected, output_actual);
|
||||
}
|
||||
}
|
||||
|
||||
void test_adjacent_difference_with_heterogeneous_types() {
|
||||
const vector<unsigned char> input = {10, 70, 20, 90};
|
||||
vector<int> output(4);
|
||||
|
||||
const auto result = adjacent_difference(input.begin(), input.end(), output.begin());
|
||||
assert(result == output.end());
|
||||
|
||||
const vector<int> expected = {10, 60, -50, 70};
|
||||
assert(output == expected);
|
||||
}
|
||||
|
||||
template <class FwdIt, class T>
|
||||
ptrdiff_t last_known_good_count(FwdIt first, FwdIt last, T v) {
|
||||
ptrdiff_t result = 0;
|
||||
|
@ -895,6 +964,18 @@ void test_swap_arrays(mt19937_64& gen) {
|
|||
}
|
||||
|
||||
void test_vector_algorithms(mt19937_64& gen) {
|
||||
test_adjacent_difference<char>(gen);
|
||||
test_adjacent_difference<signed char>(gen);
|
||||
test_adjacent_difference<unsigned char>(gen);
|
||||
test_adjacent_difference<short>(gen);
|
||||
test_adjacent_difference<unsigned short>(gen);
|
||||
test_adjacent_difference<int>(gen);
|
||||
test_adjacent_difference<unsigned int>(gen);
|
||||
test_adjacent_difference<long long>(gen);
|
||||
test_adjacent_difference<unsigned long long>(gen);
|
||||
|
||||
test_adjacent_difference_with_heterogeneous_types();
|
||||
|
||||
test_count<char>(gen);
|
||||
test_count<signed char>(gen);
|
||||
test_count<unsigned char>(gen);
|
||||
|
@ -1247,22 +1328,57 @@ void test_case_string_find_last_of(const basic_string<T>& input_haystack, const
|
|||
assert(expected == actual);
|
||||
}
|
||||
|
||||
template <class T>
|
||||
void test_case_string_find_str(const basic_string<T>& input_haystack, const basic_string<T>& input_needle) {
|
||||
ptrdiff_t expected;
|
||||
if (input_needle.empty()) {
|
||||
expected = 0;
|
||||
} else {
|
||||
const auto expected_iter = last_known_good_search(
|
||||
input_haystack.begin(), input_haystack.end(), input_needle.begin(), input_needle.end());
|
||||
|
||||
if (expected_iter != input_haystack.end()) {
|
||||
expected = expected_iter - input_haystack.begin();
|
||||
} else {
|
||||
expected = -1;
|
||||
}
|
||||
}
|
||||
const auto actual = static_cast<ptrdiff_t>(input_haystack.find(input_needle));
|
||||
assert(expected == actual);
|
||||
}
|
||||
|
||||
template <class T, class D>
|
||||
void test_basic_string_dis(mt19937_64& gen, D& dis) {
|
||||
basic_string<T> input_haystack;
|
||||
basic_string<T> input_needle;
|
||||
basic_string<T> temp;
|
||||
input_haystack.reserve(haystackDataCount);
|
||||
input_needle.reserve(needleDataCount);
|
||||
temp.reserve(needleDataCount);
|
||||
|
||||
for (;;) {
|
||||
input_needle.clear();
|
||||
|
||||
test_case_string_find_first_of(input_haystack, input_needle);
|
||||
test_case_string_find_last_of(input_haystack, input_needle);
|
||||
test_case_string_find_str(input_haystack, input_needle);
|
||||
|
||||
for (size_t attempts = 0; attempts < needleDataCount; ++attempts) {
|
||||
input_needle.push_back(static_cast<T>(dis(gen)));
|
||||
test_case_string_find_first_of(input_haystack, input_needle);
|
||||
test_case_string_find_last_of(input_haystack, input_needle);
|
||||
test_case_string_find_str(input_haystack, input_needle);
|
||||
|
||||
// For large needles the chance of a match is low, so test a guaranteed match
|
||||
if (input_haystack.size() > input_needle.size() * 2) {
|
||||
uniform_int_distribution<size_t> pos_dis(0, input_haystack.size() - input_needle.size());
|
||||
const size_t pos = pos_dis(gen);
|
||||
const auto overwritten_first = input_haystack.begin() + static_cast<ptrdiff_t>(pos);
|
||||
temp.assign(overwritten_first, overwritten_first + static_cast<ptrdiff_t>(input_needle.size()));
|
||||
copy(input_needle.begin(), input_needle.end(), overwritten_first);
|
||||
test_case_string_find_str(input_haystack, input_needle);
|
||||
copy(temp.begin(), temp.end(), overwritten_first);
|
||||
}
|
||||
}
|
||||
|
||||
if (input_haystack.size() == haystackDataCount) {
|
||||
|
|
|
@ -1,9 +1,14 @@
|
|||
// Copyright (c) Microsoft Corporation.
|
||||
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
|
||||
|
||||
#include <array>
|
||||
#include <cassert>
|
||||
#include <functional>
|
||||
#include <map>
|
||||
#include <memory>
|
||||
#include <set>
|
||||
#include <tuple>
|
||||
#include <type_traits>
|
||||
#include <unordered_map>
|
||||
#include <unordered_set>
|
||||
#include <utility>
|
||||
|
@ -142,23 +147,282 @@ void test_emplaces(FirstValue& val1, Values&... valn) {
|
|||
}
|
||||
}
|
||||
|
||||
int main() {
|
||||
// This tests that unordered_(map|set) detect cases they can avoid allocating.
|
||||
// It isn't strictly required by the standard, but we think it should be applicable to good implementations.
|
||||
template <class SetContainer>
|
||||
void test_emplace_for_unique_set() {
|
||||
int lvalueInt{42};
|
||||
test_emplaces<unordered_set<int, hash<int>, equal_to<>, TestAlloc<int>>>(lvalueInt);
|
||||
using MapType = unordered_map<int, int, hash<int>, equal_to<>, TestAlloc<pair<const int, int>>>;
|
||||
test_emplaces<MapType>(lvalueInt, lvalueInt);
|
||||
test_emplaces<SetContainer>(lvalueInt);
|
||||
}
|
||||
|
||||
template <class MapContainer>
|
||||
void test_emplace_for_unique_map() {
|
||||
int lvalueInt{42};
|
||||
test_emplaces<MapContainer>(lvalueInt, lvalueInt);
|
||||
|
||||
pair<const int, int> lvalueConstPair{42, 65};
|
||||
pair<int, int> lvaluePair{42, 65};
|
||||
pair<const int&, int> lvalueConstRefPair{lvalueInt, 65};
|
||||
pair<int&, int> lvalueRefPair{lvalueInt, 65};
|
||||
pair<const int&&, int> lvalueConstRefRefPair{move(lvalueInt), 65};
|
||||
pair<int&&, int> lvalueRefRefPair{move(lvalueInt), 65};
|
||||
test_emplaces<MapType>(lvalueConstPair);
|
||||
test_emplaces<MapType>(lvaluePair);
|
||||
test_emplaces<MapType>(lvalueConstRefPair);
|
||||
test_emplaces<MapType>(lvalueRefPair);
|
||||
test_emplaces<MapType>(lvalueConstRefRefPair);
|
||||
test_emplaces<MapType>(lvalueRefRefPair);
|
||||
test_emplaces<MapContainer>(lvalueConstPair);
|
||||
test_emplaces<MapContainer>(lvaluePair);
|
||||
test_emplaces<MapContainer>(lvalueConstRefPair);
|
||||
test_emplaces<MapContainer>(lvalueRefPair);
|
||||
test_emplaces<MapContainer>(lvalueConstRefRefPair);
|
||||
test_emplaces<MapContainer>(lvalueRefRefPair);
|
||||
|
||||
#if _HAS_CXX23
|
||||
tuple<const int, int> lvalueConstTuple{42, 65};
|
||||
tuple<int, int> lvalueTuple{42, 65};
|
||||
tuple<const int&, int> lvalueConstRefTuple{lvalueInt, 65};
|
||||
tuple<int&, int> lvalueRefTuple{lvalueInt, 65};
|
||||
tuple<const int&&, int> lvalueConstRefRefTuple{move(lvalueInt), 65};
|
||||
tuple<int&&, int> lvalueRefRefTuple{move(lvalueInt), 65};
|
||||
test_emplaces<MapContainer>(lvalueConstTuple);
|
||||
test_emplaces<MapContainer>(lvalueTuple);
|
||||
test_emplaces<MapContainer>(lvalueConstRefTuple);
|
||||
test_emplaces<MapContainer>(lvalueRefTuple);
|
||||
test_emplaces<MapContainer>(lvalueConstRefRefTuple);
|
||||
test_emplaces<MapContainer>(lvalueRefRefTuple);
|
||||
|
||||
array<int, 2> arr{42, 65};
|
||||
array<const int, 2> constArr{42, 65};
|
||||
test_emplaces<MapContainer>(arr);
|
||||
test_emplaces<MapContainer>(constArr);
|
||||
#endif // _HAS_CXX23
|
||||
|
||||
tuple<int> tupleIntSixtyFive{65};
|
||||
|
||||
tuple<const int> lvalueConstOneTuple{42};
|
||||
tuple<int> lvalueOneTuple{42};
|
||||
tuple<const int&> lvalueConstRefOneTuple{lvalueInt};
|
||||
tuple<int&> lvalueRefOneTuple{lvalueInt};
|
||||
test_emplaces<MapContainer>(piecewise_construct, lvalueConstOneTuple, tupleIntSixtyFive);
|
||||
test_emplaces<MapContainer>(piecewise_construct, lvalueOneTuple, tupleIntSixtyFive);
|
||||
test_emplaces<MapContainer>(piecewise_construct, lvalueConstRefOneTuple, tupleIntSixtyFive);
|
||||
test_emplaces<MapContainer>(piecewise_construct, lvalueRefOneTuple, tupleIntSixtyFive);
|
||||
}
|
||||
|
||||
// also test that the optimization strategy does not mishandle volatile arguments
|
||||
template <class SetContainer>
|
||||
void test_volatile_arguments_for_unique_set() {
|
||||
using Key = typename SetContainer::value_type;
|
||||
|
||||
SetContainer s;
|
||||
|
||||
volatile Key x = 0;
|
||||
const volatile Key& cx = x;
|
||||
|
||||
s.emplace(x);
|
||||
x = 1;
|
||||
s.emplace(move(x));
|
||||
x = 2;
|
||||
s.emplace(cx);
|
||||
x = 3;
|
||||
s.emplace(move(cx));
|
||||
|
||||
x = 4;
|
||||
s.emplace_hint(s.end(), x);
|
||||
x = 5;
|
||||
s.emplace_hint(s.end(), move(x));
|
||||
x = 6;
|
||||
s.emplace_hint(s.end(), cx);
|
||||
x = 7;
|
||||
s.emplace_hint(s.end(), move(cx));
|
||||
|
||||
assert((s == SetContainer{0, 1, 2, 3, 4, 5, 6, 7}));
|
||||
}
|
||||
|
||||
template <class MapContainer, class PairLike>
|
||||
void test_pair_like_volatile_for_unique_map() {
|
||||
using First = tuple_element_t<0, PairLike>;
|
||||
using Second = tuple_element_t<1, PairLike>;
|
||||
|
||||
MapContainer m;
|
||||
|
||||
volatile remove_cv_t<remove_reference_t<First>> x = 0;
|
||||
{
|
||||
PairLike p{static_cast<First>(x), Second{}};
|
||||
m.emplace(p);
|
||||
}
|
||||
|
||||
x = 1;
|
||||
{
|
||||
PairLike p{static_cast<First>(x), Second{}};
|
||||
m.emplace(move(p));
|
||||
}
|
||||
|
||||
x = 2;
|
||||
{
|
||||
PairLike p{static_cast<First>(x), Second{}};
|
||||
const auto& cp = p;
|
||||
m.emplace(cp);
|
||||
}
|
||||
|
||||
x = 3;
|
||||
{
|
||||
PairLike p{static_cast<First>(x), Second{}};
|
||||
const auto& cp = p;
|
||||
m.emplace(move(cp));
|
||||
}
|
||||
|
||||
x = 4;
|
||||
{
|
||||
PairLike p{static_cast<First>(x), Second{}};
|
||||
m.emplace_hint(m.end(), p);
|
||||
}
|
||||
|
||||
x = 5;
|
||||
{
|
||||
PairLike p{static_cast<First>(x), Second{}};
|
||||
m.emplace_hint(m.end(), move(p));
|
||||
}
|
||||
|
||||
x = 6;
|
||||
{
|
||||
PairLike p{static_cast<First>(x), Second{}};
|
||||
const auto& cp = p;
|
||||
m.emplace_hint(m.end(), cp);
|
||||
}
|
||||
|
||||
x = 7;
|
||||
{
|
||||
PairLike p{static_cast<First>(x), Second{}};
|
||||
const auto& cp = p;
|
||||
m.emplace_hint(m.end(), move(cp));
|
||||
}
|
||||
|
||||
assert((m == MapContainer{{0, {}}, {1, {}}, {2, {}}, {3, {}}, {4, {}}, {5, {}}, {6, {}}, {7, {}}}));
|
||||
}
|
||||
|
||||
template <class MapContainer, class Tuple>
|
||||
void test_piecewise_volatile_for_unique_map() {
|
||||
using First = tuple_element_t<0, Tuple>;
|
||||
|
||||
MapContainer m;
|
||||
|
||||
volatile remove_cv_t<remove_reference_t<First>> x = 0;
|
||||
{
|
||||
Tuple tp{static_cast<First>(x)};
|
||||
m.emplace(piecewise_construct, tp, tuple<>{});
|
||||
}
|
||||
|
||||
x = 1;
|
||||
{
|
||||
Tuple tp{static_cast<First>(x)};
|
||||
m.emplace(piecewise_construct, move(tp), tuple<>{});
|
||||
}
|
||||
|
||||
x = 2;
|
||||
{
|
||||
Tuple tp{static_cast<First>(x)};
|
||||
const auto& ctp = tp;
|
||||
m.emplace(piecewise_construct, ctp, tuple<>{});
|
||||
}
|
||||
|
||||
x = 3;
|
||||
{
|
||||
Tuple tp{static_cast<First>(x)};
|
||||
const auto& ctp = tp;
|
||||
m.emplace(piecewise_construct, move(ctp), tuple<>{});
|
||||
}
|
||||
|
||||
x = 4;
|
||||
{
|
||||
Tuple tp{static_cast<First>(x)};
|
||||
m.emplace_hint(m.end(), piecewise_construct, tp, tuple<>{});
|
||||
}
|
||||
|
||||
x = 5;
|
||||
{
|
||||
Tuple tp{static_cast<First>(x)};
|
||||
m.emplace_hint(m.end(), piecewise_construct, move(tp), tuple<>{});
|
||||
}
|
||||
|
||||
x = 6;
|
||||
{
|
||||
Tuple tp{static_cast<First>(x)};
|
||||
const auto& ctp = tp;
|
||||
m.emplace_hint(m.end(), piecewise_construct, ctp, tuple<>{});
|
||||
}
|
||||
|
||||
x = 7;
|
||||
{
|
||||
Tuple tp{static_cast<First>(x)};
|
||||
const auto& ctp = tp;
|
||||
m.emplace_hint(m.end(), piecewise_construct, move(ctp), tuple<>{});
|
||||
}
|
||||
|
||||
assert((m == MapContainer{{0, {}}, {1, {}}, {2, {}}, {3, {}}, {4, {}}, {5, {}}, {6, {}}, {7, {}}}));
|
||||
}
|
||||
|
||||
template <class MapContainer>
|
||||
void test_volatile_arguments_for_unique_map() {
|
||||
using Key = typename MapContainer::key_type;
|
||||
using Mapped = typename MapContainer::mapped_type;
|
||||
|
||||
{
|
||||
volatile Key x = 0;
|
||||
const volatile Key& cx = x;
|
||||
|
||||
MapContainer m;
|
||||
|
||||
m.emplace(x, Mapped{});
|
||||
x = 1;
|
||||
m.emplace(move(x), Mapped{});
|
||||
x = 2;
|
||||
m.emplace(cx, Mapped{});
|
||||
x = 3;
|
||||
m.emplace(move(cx), Mapped{});
|
||||
|
||||
x = 4;
|
||||
m.emplace_hint(m.end(), x, Mapped{});
|
||||
x = 5;
|
||||
m.emplace_hint(m.end(), move(x), Mapped{});
|
||||
x = 6;
|
||||
m.emplace_hint(m.end(), cx, Mapped{});
|
||||
x = 7;
|
||||
m.emplace_hint(m.end(), move(cx), Mapped{});
|
||||
|
||||
assert((m == MapContainer{{0, {}}, {1, {}}, {2, {}}, {3, {}}, {4, {}}, {5, {}}, {6, {}}, {7, {}}}));
|
||||
}
|
||||
|
||||
test_pair_like_volatile_for_unique_map<MapContainer, pair<volatile Key, Mapped>>();
|
||||
test_pair_like_volatile_for_unique_map<MapContainer, pair<const volatile Key, Mapped>>();
|
||||
test_pair_like_volatile_for_unique_map<MapContainer, pair<volatile Key&, Mapped>>();
|
||||
test_pair_like_volatile_for_unique_map<MapContainer, pair<const volatile Key&, Mapped>>();
|
||||
test_pair_like_volatile_for_unique_map<MapContainer, pair<volatile Key&&, Mapped>>();
|
||||
test_pair_like_volatile_for_unique_map<MapContainer, pair<const volatile Key&&, Mapped>>();
|
||||
#if _HAS_CXX23
|
||||
test_pair_like_volatile_for_unique_map<MapContainer, tuple<volatile Key, Mapped>>();
|
||||
test_pair_like_volatile_for_unique_map<MapContainer, tuple<const volatile Key, Mapped>>();
|
||||
test_pair_like_volatile_for_unique_map<MapContainer, tuple<volatile Key&, Mapped>>();
|
||||
test_pair_like_volatile_for_unique_map<MapContainer, tuple<const volatile Key&, Mapped>>();
|
||||
test_pair_like_volatile_for_unique_map<MapContainer, tuple<volatile Key&&, Mapped>>();
|
||||
test_pair_like_volatile_for_unique_map<MapContainer, tuple<const volatile Key&&, Mapped>>();
|
||||
|
||||
test_pair_like_volatile_for_unique_map<MapContainer, array<volatile Key, 2>>();
|
||||
test_pair_like_volatile_for_unique_map<MapContainer, array<const volatile Key, 2>>();
|
||||
#endif // _HAS_CXX23
|
||||
|
||||
test_piecewise_volatile_for_unique_map<MapContainer, tuple<volatile Key>>();
|
||||
test_piecewise_volatile_for_unique_map<MapContainer, tuple<const volatile Key>>();
|
||||
test_piecewise_volatile_for_unique_map<MapContainer, tuple<volatile Key&>>();
|
||||
test_piecewise_volatile_for_unique_map<MapContainer, tuple<const volatile Key&>>();
|
||||
}
|
||||
|
||||
int main() {
|
||||
// This tests that unordered_(map|set) detect cases they can avoid allocating.
|
||||
// It isn't strictly required by the standard, but we think it should be applicable to good implementations.
|
||||
test_emplace_for_unique_set<set<int, less<>, TestAlloc<int>>>();
|
||||
test_emplace_for_unique_set<unordered_set<int, hash<int>, equal_to<>, TestAlloc<int>>>();
|
||||
|
||||
test_emplace_for_unique_map<map<int, int, less<>, TestAlloc<pair<const int, int>>>>();
|
||||
test_emplace_for_unique_map<unordered_map<int, int, hash<int>, equal_to<>, TestAlloc<pair<const int, int>>>>();
|
||||
|
||||
test_volatile_arguments_for_unique_set<set<int>>();
|
||||
test_volatile_arguments_for_unique_set<unordered_set<int>>();
|
||||
|
||||
test_volatile_arguments_for_unique_map<map<int, long>>();
|
||||
test_volatile_arguments_for_unique_map<unordered_map<int, long>>();
|
||||
}
|
||||
|
|
Загрузка…
Ссылка в новой задаче