I wrote a simple lambda function to copy one static size array into another
#include <tuple>
#include <iostream>
int main() {
constexpr auto copyArray = [](auto& out, const auto& in) noexcept {
[&]<size_t... Idxs>(std::index_sequence<Idxs...>) noexcept {
((out[Idxs]=in[Idxs]), ...);
}(std::make_index_sequence<std::min(std::size(out), std::size(in))>());
};
constexpr char c1[] = "dfjkwhjifehuiqh3iuhuihf3enewlknek32 ";
char c2[255]{0};
copyArray(c2, c1);
std::cout << (std::string(c2) == std::string(c1)) << "\n";
}
It works fine with GCC, but fails to compile with clang/msvc.
The error says non-type template argument is not a constant expression
, which basically means clang can't figure out the size of in
and out
, but only if use std::size
, sizeof
gives the right result (the compiler understands "the real" size of the arrays).
As I understand both in
and out
defined as references and should be deduced as char(&)[40]
and char(&)[255]
, so the size should be visible at compile time.
Is it clang/msvc issue?
This is my favorite weird constexpr issue: the constexpr array size problem resolved by P2280 (note: this is my blog post and my paper).
The short version is that std::size(in)
didn't used to be valid for use in a constant expression simply because in
is a reference that is unknown to the constant evaluator. That's now changed - you don't actually need to read through in
in any way to find the answer, so there's nothing that should prevent that from working. gcc apparently implements the fix here, clang does not yet.
Until then, you can change the lambda declaration to:
constexpr auto copyArray = []<class T, size_t M, size_t M>(T (&out)[M], T const (&in)[M]) noexcept {
And then use M
and N
instead of std::size(out)
and std::size(in)
, respectively. Or any number of other workarounds (including just using decltype(in)
and passing that to std::extent
, etc.)