Describe the bug
std::find and std::ranges::find (both standardized in <algorithm> but implemented in <xutility>) have long been able to handle structs with defaulted equality comparison (behavior extended in the C++20 standard). However, recent optimizations regarding LLVM usage have resulted in a regression (#5479 and specifically https://github.com/microsoft/STL/pull/5767/changes#diff-980098f27df5ad089b2a93a8489574cfcab50f4b9d9c55524b0a57444a4c664eR6137-R6138). In particular, types with a sizeof other than 1, 2, 4 or 8, that have a defaulted equality comparison operator, fail to compile.
This was checked using the latest version of the STL shipped with Visual Studio 18.6.0, which seems to be 19.51.36231, and Clang-CL version 20.1.8.
Command-line test case
C:\Temp>type repro.cpp
#include <algorithm>
#include <cstdint>
#include <vector>
namespace {
struct A {
int64_t a{};
int64_t b{};
friend constexpr bool operator==(A x, A y) = default;
};
struct B {
int64_t a{};
int64_t b{};
friend constexpr bool operator==(B x, B y) {
return x.a == y.a && x.b == y.b;
}
};
} // namespace
int main() {
// the following calls fail to compile due to defaulted comparison operator
const auto a = std::vector<A>{};
std::ignore = std::find(a.begin(), a.end(), A{}); // does not compile
std::ignore = std::ranges::find(a, A{}); // does not compile
// user-defined comparison operator works fine
const auto b = std::vector<B>{};
std::ignore = std::find(b.begin(), b.end(), B{}); // OK
std::ignore = std::ranges::find(b, B{}); // OK
return 0;
}
C:\Temp>clang-cl -clang:-std=c++23 -- repro.cpp
In file included from repro.cpp:1:
In file included from C:\Program Files\Microsoft Visual Studio\18\Enterprise\VC\Tools\MSVC\14.51.36231\include\algorithm:10:
In file included from C:\Program Files\Microsoft Visual Studio\18\Enterprise\VC\Tools\MSVC\14.51.36231\include\__msvc_heap_algorithms.hpp:11:
C:\Program Files\Microsoft Visual Studio\18\Enterprise\VC\Tools\MSVC\14.51.36231\include\xutility(320,23): error: static assertion failed: unexpected size
320 | static_assert(false, "unexpected size");
| ^~~~~
C:\Program Files\Microsoft Visual Studio\18\Enterprise\VC\Tools\MSVC\14.51.36231\include\xutility(6445,42): note: in instantiation of function template specialization 'std::_Find_vectorized<const (anonymous namespace)::A,
(anonymous namespace)::A>' requested here
6445 | const auto _Result = _STD _Find_vectorized(_First_ptr, _STD _To_address(_Last), _Val);
| ^
1 error generated
NOTE: the above script compiles just fine with MSVC shipped with the same version of Visual Studio
C:\Temp>cl /EHsc /W4 /WX /std:c++latest .\repro.cpp
Microsoft (R) C/C++ Optimizing Compiler Version 19.51.36243 for x64
Copyright (C) Microsoft Corporation. All rights reserved.
/std:c++latest is provided as a preview of language features from the latest C++
working draft, and we're eager to hear about bugs and suggestions for improvements.
However, note that these features are provided as-is without support, and subject
to changes or removal as the working draft evolves. See
https://go.microsoft.com/fwlink/?linkid=2045807 for details.
repro.cpp
Microsoft (R) Incremental Linker Version 14.51.36243.0
Copyright (C) Microsoft Corporation. All rights reserved.
/out:repro.exe
repro.obj
I found that the vectorization is only implemented on 1-, 2-, 4- or 8-bytes objects, and that all other sizes trigger the above error: static assertion failed: unexpected size.
However, because _Vector_alg_in_find_is_safe_elem under the hood uses __is_trivially_equality_comparable, which evaluates to true, it takes the vectorized branch anyways, which, in turn, triggers the static assertion.
Expected behavior
std::find and std::ranges::find work on types with comparison operator for both Clang-CL and MSVC, regardless of whether it was defaulted or explicitly specified.
Either:
STL version
Additional context
- This was found in our Nightly build on GitHub (https://github.com/PowerGridModel/power-grid-model/actions/runs/26138748913/attempts/1) which runs on the
msvc-latest GitHub Actions runner image. Now that the latest Visual Studio is being deployed in the GitHub Actions runner images (https://github.com/actions/runner-images/blob/win25-vs2026/20260518.113/images/windows/Windows2025-VS2026-Readme.md), this will affect more users.
- https://en.cppreference.com/cpp/language/default_comparisons contains an example with a struct similar to what was used here, so that suggests that it really is something that is supposed to be supported.
std::pair<int, int> does not suffer from this issue (contrary to what is suggested in https://github.com/microsoft/STL/issues/54790) because it does not have a defaulted equality comparison operator:
_EXPORT_STD template <class _Ty1, class _Ty2, class _Uty1, class _Uty2>
_NODISCARD constexpr bool operator==(const pair<_Ty1, _Ty2>& _Left, const pair<_Uty1, _Uty2>& _Right) {
return _Left.first == _Right.first && _Left.second == _Right.second;
}
This also implies that std::pair<int, int> at the moment is not vectorized.
- If I understand the libcxx implementation of
find for types satisfying __is_trivially_equality_comparable_v (https://github.com/llvm/llvm-project/blob/main/libcxx/include/__algorithm/find.h#L137-L139) correctly, then it looks like LLVM opts for the exclusion of vectorization for types with sizes other than 1 or sizeof(wchar) and that also are not integral types (which effectively excludes more than aforementioned approach _Vector_alg_in_find_is_safe needs to explicitly exclude those odd-sized and/or large types.)
Describe the bug
std::findandstd::ranges::find(both standardized in<algorithm>but implemented in<xutility>) have long been able to handle structs with defaulted equality comparison (behavior extended in the C++20 standard). However, recent optimizations regarding LLVM usage have resulted in a regression (#5479 and specifically https://github.com/microsoft/STL/pull/5767/changes#diff-980098f27df5ad089b2a93a8489574cfcab50f4b9d9c55524b0a57444a4c664eR6137-R6138). In particular, types with asizeofother than 1, 2, 4 or 8, that have a defaulted equality comparison operator, fail to compile.This was checked using the latest version of the STL shipped with Visual Studio
18.6.0, which seems to be19.51.36231, and Clang-CL version20.1.8.Command-line test case
NOTE: the above script compiles just fine with MSVC shipped with the same version of Visual Studio
I found that the vectorization is only implemented on 1-, 2-, 4- or 8-bytes objects, and that all other sizes trigger the above
error: static assertion failed: unexpected size.However, because
_Vector_alg_in_find_is_safe_elemunder the hood uses__is_trivially_equality_comparable, which evaluates totrue, it takes the vectorized branch anyways, which, in turn, triggers the static assertion.Expected behavior
std::findandstd::ranges::findwork on types with comparison operator for both Clang-CL and MSVC, regardless of whether it was defaulted or explicitly specified.Either:
_Vector_alg_in_find_is_safeneeds to explicitly exclude those odd-sized and/or large types.STL version
Option 2:
_MSVC_STL_UPDATEvalue (because Clang-CL is used)For completeness:
cl.exeshowsMicrosoft (R) C/C++ Optimizing Compiler Version 19.51.36243 for x64Additional context
msvc-latestGitHub Actions runner image. Now that the latest Visual Studio is being deployed in the GitHub Actions runner images (https://github.com/actions/runner-images/blob/win25-vs2026/20260518.113/images/windows/Windows2025-VS2026-Readme.md), this will affect more users.std::pair<int, int>does not suffer from this issue (contrary to what is suggested in https://github.com/microsoft/STL/issues/54790) because it does not have a defaulted equality comparison operator:std::pair<int, int>at the moment is not vectorized.findfor types satisfying__is_trivially_equality_comparable_v(https://github.com/llvm/llvm-project/blob/main/libcxx/include/__algorithm/find.h#L137-L139) correctly, then it looks like LLVM opts for the exclusion of vectorization for types with sizes other than1orsizeof(wchar)and that also are not integral types (which effectively excludes more than aforementioned approach_Vector_alg_in_find_is_safe needs to explicitly exclude those odd-sized and/or large types.)