Skip to content
17 changes: 8 additions & 9 deletions stl/inc/initializer_list
Original file line number Diff line number Diff line change
Expand Up @@ -51,20 +51,19 @@ public:
return static_cast<size_t>(_Last - _First);
}

_NODISCARD constexpr bool empty() const noexcept {
return size() == 0;
}

_NODISCARD constexpr const _Elem* data() const noexcept {
return begin();
}

private:
const _Elem* _First;
const _Elem* _Last;
};

_EXPORT_STD template <class _Elem>
_NODISCARD constexpr const _Elem* begin(initializer_list<_Elem> _Ilist) noexcept {
return _Ilist.begin();
}

_EXPORT_STD template <class _Elem>
_NODISCARD constexpr const _Elem* end(initializer_list<_Elem> _Ilist) noexcept {
return _Ilist.end();
}
_STD_END

// TRANSITION, non-_Ugly attribute tokens
Expand Down
52 changes: 19 additions & 33 deletions stl/inc/valarray
Original file line number Diff line number Diff line change
Expand Up @@ -59,19 +59,9 @@ class valarray { // store array with various indexing options
public:
friend _Tidy_deallocate_guard<valarray>;

template <class _Ty2>
friend _Ty2* begin(valarray<_Ty2>& _Array) noexcept /* strengthened */;

template <class _Ty2>
friend const _Ty2* begin(const valarray<_Ty2>& _Array) noexcept /* strengthened */;

template <class _Ty2>
friend _Ty2* end(valarray<_Ty2>& _Array) noexcept /* strengthened */;

template <class _Ty2>
friend const _Ty2* end(const valarray<_Ty2>& _Array) noexcept /* strengthened */;

using value_type = _Ty;
using value_type = _Ty;
using iterator = _Ty*;
using const_iterator = const _Ty*;

valarray() = default; // construct empty valarray

Expand Down Expand Up @@ -411,6 +401,22 @@ public:
return _Mysize;
}

_NODISCARD iterator begin() noexcept /* strengthened */ {
return _Myptr;
}

_NODISCARD const_iterator begin() const noexcept /* strengthened */ {
return _Myptr;
}

_NODISCARD iterator end() noexcept /* strengthened */ {
return _Myptr + _Mysize;
}

_NODISCARD const_iterator end() const noexcept /* strengthened */ {
return _Myptr + _Mysize;
}

_NODISCARD const _Ty& operator[](size_t _Off) const noexcept /* strengthened */ {
#if _MSVC_STL_HARDENING_VALARRAY || _ITERATOR_DEBUG_LEVEL != 0
_STL_VERIFY(_Off < _Mysize, "valarray subscript out of range");
Expand Down Expand Up @@ -612,26 +618,6 @@ void swap(valarray<_Ty>& _Left, valarray<_Ty>& _Right) noexcept {
_Left.swap(_Right);
}

_EXPORT_STD template <class _Ty>
_NODISCARD _Ty* begin(valarray<_Ty>& _Array) noexcept /* strengthened */ {
return _Array._Myptr;
}

_EXPORT_STD template <class _Ty>
_NODISCARD const _Ty* begin(const valarray<_Ty>& _Array) noexcept /* strengthened */ {
return _Array._Myptr;
}

_EXPORT_STD template <class _Ty>
_NODISCARD _Ty* end(valarray<_Ty>& _Array) noexcept /* strengthened */ {
return _Array._Myptr + _Array.size();
}

_EXPORT_STD template <class _Ty>
_NODISCARD const _Ty* end(const valarray<_Ty>& _Array) noexcept /* strengthened */ {
return _Array._Myptr + _Array.size();
}

_EXPORT_STD template <class _Ty>
_NODISCARD valarray<_Ty> operator*(const valarray<_Ty>& _Left, const typename valarray<_Ty>::value_type& _Right) {
const size_t _Size = _Left.size();
Expand Down
10 changes: 0 additions & 10 deletions stl/inc/xutility
Original file line number Diff line number Diff line change
Expand Up @@ -2283,11 +2283,6 @@ _NODISCARD_EMPTY_NON_MEMBER constexpr bool empty(const _Ty (&)[_Size]) noexcept
return false;
}

_EXPORT_STD template <class _Elem>
_NODISCARD_EMPTY_NON_MEMBER constexpr bool empty(initializer_list<_Elem> _Ilist) noexcept {
return _Ilist.size() == 0;
}

_EXPORT_STD template <class _Container>
_NODISCARD constexpr auto data(_Container& _Cont) noexcept(noexcept(_Cont.data())) /* strengthened */
-> decltype(_Cont.data()) {
Expand All @@ -2305,11 +2300,6 @@ _NODISCARD constexpr _Ty* data(_Ty (&_Array)[_Size]) noexcept {
return _Array;
}

_EXPORT_STD template <class _Elem>
_NODISCARD constexpr const _Elem* data(initializer_list<_Elem> _Ilist) noexcept {
return _Ilist.begin();
}

#if _HAS_CXX20
#if _HAS_CXX23
_EXPORT_STD template <indirectly_readable _Ty>
Expand Down
5 changes: 5 additions & 0 deletions stl/inc/yvals_core.h
Original file line number Diff line number Diff line change
Expand Up @@ -80,6 +80,7 @@
// (__cpp_lib_freestanding_algorithm and __cpp_lib_freestanding_array only)
// P2937R0 Freestanding Library: Remove strtok
// P2968R2 Make std::ignore A First-Class Object
// P3016R6 Resolve Inconsistencies In begin/end For valarray And Braced Initializer Lists
// P3223R2 Making istream::ignore() Less Surprising
// P3323R1 Forbid atomic<cv T>, Specify atomic_ref<cv T>
// (for atomic<cv T>)
Expand Down Expand Up @@ -1833,6 +1834,10 @@ _EMIT_STL_ERROR(STL1004, "C++98 unexpected() is incompatible with C++23 unexpect
#define __cpp_lib_unreachable 202202L
#endif // _HAS_CXX23

// C++26
#define __cpp_lib_initializer_list 202511L
#define __cpp_lib_valarray 202511L
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

These should be moved to the C++14 section. And we should test them in tests/std/tests/VSO_0157762_feature_test_macros/test.compile.pass.cpp.

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Understood. Individual parts of yvals_core.h are sorted alphabetically, right? So I should prefer alphabetical sorting over smaller diff?
Tests in tests/std/tests/VSO_0157762_feature_test_macros/test.compile.pass.cpp don't seem to be sorted, so I will just add new lines to the end.


// macros with language mode sensitivity
#if _HAS_CXX20
#define __cpp_lib_array_constexpr 201811L // P1032R1 Miscellaneous constexpr
Expand Down
3 changes: 3 additions & 0 deletions tests/libcxx/expected_results.txt
Original file line number Diff line number Diff line change
Expand Up @@ -142,6 +142,9 @@ std/language.support/support.limits/support.limits.general/mdspan.version.compil
# libc++ has not implemented P2937R0: "Freestanding Library: Remove strtok"
std/language.support/support.limits/support.limits.general/cstring.version.compile.pass.cpp FAIL

# libc++ has not implemented P3016R6: "Resolve Inconsistencies In begin/end For valarray And Braced Initializer Lists"
std/language.support/support.initlist/support.initlist.range/begin_end.pass.cpp FAIL

# libc++ has not implemented P3323R1: "Forbid atomic<cv T>, Specify atomic_ref<cv T>"
std/atomics/atomics.ref/member_types.compile.pass.cpp FAIL

Expand Down
1 change: 1 addition & 0 deletions tests/std/test.lst
Original file line number Diff line number Diff line change
Expand Up @@ -720,6 +720,7 @@ tests\P2693R1_text_formatting_header_stacktrace
tests\P2693R1_text_formatting_header_thread
tests\P2693R1_text_formatting_stacktrace
tests\P2693R1_text_formatting_thread_id
tests\P3016R6_inconsistent_begin_end
tests\P3107R5_enabled_specializations
tests\P3349R1_contiguous_iterators_to_pointers
tests\P3503R3_packaged_task_promise_with_allocator
Expand Down
2 changes: 2 additions & 0 deletions tests/std/tests/Dev11_1074023_constexpr/test.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -53,7 +53,9 @@ constexpr auto int64_max = numeric_limits<int64_t>::max();
constexpr initializer_list<int> il;
STATIC_ASSERT(il.size() == 0);
STATIC_ASSERT(il.begin() == il.end());
#if _HAS_CXX17
STATIC_ASSERT(begin(il) == end(il));
#endif // _HAS_CXX17
Copy link
Contributor

@frederick-vs-ja frederick-vs-ja Nov 13, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

For archaeology: constexpr for initializer_list was added to C++14 via WG21-N3471, while constexpr for free std::begin/std::end was added to C++17 via WG21-P0031R0.

I'm a bit hesitant now as the changes would disallow executing for (int n : {1, 2, 3}) { /* ... */ } in constant evaluation in C++14 mode, while WG21-N3471 intentionally allowed this.

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

No language lawyer here, so correct me if I am wrong, but the range-based for loop does not use the free function in this case, but rather the member begin/end. Those are marked as constexpr unconditionally (not only in C++14 onwards) in code.

Link to cppinsights C++14 mode (very powerful argument for normal developers, not sure whether it is appropriate for STL development 😅 ) https://cppinsights.io/s/faddcfbd

Nevertheless, I have added the for loop to the test suite and it compiles and executes in all modes.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

No language lawyer here, so correct me if I am wrong, but the range-based for loop does not use the free function in this case, but rather the member begin/end. Those are marked as constexpr unconditionally (not only in C++14 onwards) in code.

Oh, you are right. Sorry. I must have been be confused with some constexpr reduction.


// TRANSITION,
// constexpr error_category() noexcept;
Expand Down
4 changes: 4 additions & 0 deletions tests/std/tests/P3016R6_inconsistent_begin_end/env.lst
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
# Copyright (c) Microsoft Corporation.
# SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception

RUNALL_INCLUDE ..\usual_latest_matrix.lst
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The changes should be tested in C++14 mode.

Suggested change
RUNALL_INCLUDE ..\usual_latest_matrix.lst
RUNALL_INCLUDE ..\usual_matrix.lst

Also, static_assert(e); is only standardized since C++17, so we need to write either static_assert(e, ""); or #define STATIC_ASSERT(__VA_ARGS__) static_assert(__VA_ARGS__, #__VA_ARGS__). The latter is widely used in MSVC STL's test suite.

65 changes: 65 additions & 0 deletions tests/std/tests/P3016R6_inconsistent_begin_end/test.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
// Copyright (c) Microsoft Corporation.
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception

#include <initializer_list>
#include <iterator>
#include <type_traits>
#include <valarray>

namespace my {

template <typename T>
void begin(std::initializer_list<T>);

template <typename T>
void end(std::initializer_list<T>);

template <typename T>
void empty(std::initializer_list<T>);

template <typename T>
void data(std::initializer_list<T>);
} // namespace my


int main() {
{
// Check that free functions in std can't be invoked with braced-initializer-list.
// If they could be invoked, the following expressions would be ambiguous between std:: and my::
using namespace std;
using namespace my;
static_assert(std::is_same_v<decltype(begin({1, 2, 3})), void>);
static_assert(std::is_same_v<decltype(end({1, 2, 3})), void>);
static_assert(std::is_same_v<decltype(empty({1, 2, 3})), void>);
static_assert(std::is_same_v<decltype(data({1, 2, 3})), void>);
}

{
// Check that free functions in std still can be invoked on std::initializer_list
std::initializer_list<int> il = {1, 2, 3};

using namespace std;
(void) begin(il);
(void) cbegin(il);
(void) end(il);
(void) cend(il);
(void) size(il);
(void) empty(il);
(void) data(il);
}

{
// Check that free functions in std can be invoked on std::valarray
std::valarray<int> v{1};

using namespace std;
(void) begin(v);
(void) cbegin(v); // Did not compile before P3016R6
(void) end(v);
(void) cend(v); // Did not compile before P3016R6
(void) size(v);
// There are no members 'empty' and 'data' of valarray
// (void) empty(il);
// (void) data(il);
}
}