TLDR Getting inspiration from several answers, the final proposal at the bottom performs the static check and, in case of failure, gives a diagnosis indicating the first failing index for gcc, clang and msvc.
@Joel answer seems OK.
Yet here is a solution valid for C++17 also:
#include <string_view>
using namespace std::string_view_literals;
constexpr std::string_view arr[] = {
"ab"sv,
"xyz"sv,
};
template <std::size_t... Is>
constexpr void CheckImpl(std::index_sequence<Is...>&&) {
constexpr bool test = ((arr[Is] == "xyz") && ...);
static_assert(test, "test failed");
};
constexpr auto Check() {
using ind_t = std::make_index_sequence<std::size(arr)>;
CheckImpl(ind_t{});
}
int main() {
Check();
return 0;
}
LIVE
@Joel comment on i being not usable in constant expression is accurate. Here the technics uses std::make_index_sequence to retrieve a compile-time list of index (in the form of the template non-type pack std::size_t... Is) and then use a fold expression to apply the test on all indices: ((arr[Is] == "xyz") && ...).
I'm nearly sure that they are now more elegant ways to do that but the technic is generic and can be used in many situations.
In the spirit of @Marek solution, CheckImpl can be modified to give the first failing index:
template <std::size_t... Is>
constexpr void CheckImpl(std::index_sequence<Is...>&&) {
constexpr std::size_t i = []() {
std::size_t n = 0;
bool test = ((++n, arr[Is] == "xyz") && ...);
return test?n:n-1;
}();
static_assert(i == sizeof...(Is), "test failed");
};
LIVE
In the fold expression I increment a counter, the folding will stop as soon as a test is failing.
If no test fails, n will be sizeof...(Is) and I'm returning it. Overwise it is the index of the failing element +1, so I'm returning n-1.
A possible compiler (clang/gcc) output is:
note: the comparison reduces to '(0 == 2)'
Which indicates that elements 0 does not respect the assertion.
Unfortunatly, msvc is not that verbose.
Final solution using a single lambda as in @WeijunZhou answer and my solution to find the first failing index, improving the diagnosis by using a helper template struct that cannot be instantiated when the given array is invalid:
#include <iostream>
#include <string_view>
using namespace std::string_view_literals;
constexpr std::string_view arr[] = {
"xyz"sv,
"xyz"sv,
"ab"sv,
"xyz"sv,
};
// helper struct that fails to instantiate if Ind != N
template <std::size_t Ind, std::size_t N>
struct FailedAtIndForN {
static_assert(N < 0);
};
// specialization for the case where the array test succeeded and Ind == N
template <std::size_t X>
struct FailedAtIndForN<X, X> {};
int main() {
[]<auto... Is>(std::index_sequence<Is...>) {
constexpr std::size_t i = []() {
std::size_t n = 0;
bool test = ((++n, arr[Is] == "xyz") && ...);
return test ? n : n - 1;
}();
[[maybe_unused]] static constexpr FailedAtIndForN<i, sizeof...(Is)>
failure;
}(std::make_index_sequence<std::size(arr)>());
return 0;
}
LIVE
As above, the possibly failing index is constant evaluated, using a nested lambda. Then I try to instantiate FailedAtIndForN which will fail if the returned index is not the size of the array.
In this case, in each tested compiler output there is, for instance, 'FailedAtIndForN<2, 4>', meaning that the array has a wrong value at index 2 for a total size of 4.
NB1: with c++26 it might be easier to use a static_assert with a constant expression as assertion message indicating a failure to instantiate.
NB2: for the record, i is in fact not necessary (except for readability) as 'FailedAtIndForN` can take the immediately invoked lambda as first template argument LIVE.
constexprfunction, e.g.static_assert(std::ranges::equal(arr, std::array{"ab", "xyz"}));static_assertlooks like minimum C++17.static_assert(std::count(begin(arr), end(arr), "ab"sv) == std::ssize(arr));? godbolt.org/z/zYhEevYTjstatic_assertwas just an example, and they want in general to perform any compile time operation on the array elements.