Currently, trying to serialize an object that can’t be serialized will fail with a short error message. For example, the diff and the error message:
 0diff --git a/src/test/serialize_tests.cpp b/src/test/serialize_tests.cpp
 1index d75eb499b4..773f49845b 100644
 2--- a/src/test/serialize_tests.cpp
 3+++ b/src/test/serialize_tests.cpp
 4@@ -62,6 +62,8 @@ public:
 5 
 6 BOOST_AUTO_TEST_CASE(sizes)
 7 {
 8+    int b[4];
 9+    DataStream{} << b << Span{b};
10     BOOST_CHECK_EQUAL(sizeof(unsigned char), GetSerializeSize((unsigned char)0));
11     BOOST_CHECK_EQUAL(sizeof(int8_t), GetSerializeSize(int8_t(0)));
12     BOOST_CHECK_EQUAL(sizeof(uint8_t), GetSerializeSize(uint8_t(0)));
0./serialize.h:765:6: error: member reference base type 'const int[4]' is not a structure or union
1  765 |     a.Serialize(os);
2      |     ~^~~~~~~~~~
0./serialize.h:277:109: error: no matching function for call to 'UCharCast'
1  277 | template <typename Stream, typename B> void Serialize(Stream& s, Span<B> span) { (void)/* force byte-type */UCharCast(span.data()); s.write(AsBytes(span)); }
2      |                                                                                                             ^~~~~~~~~
This is fine. However, it would be more helpful for developers and more accurate by the compiler to explain why each function is not selected.
Fix this by using C++20 concepts where appropriate.