I understand that C++20 has modified the definition of aggregate.
In the following code, we have a Packet class with just a fixed-size array of bytes (more than 4 in the real code). It must match the binary representation, including for arrays and vectors of Packet. So, we cannot define some higher level of abstraction, just keep the low-level representation. And, because we manipulate constant packets, it must be initialized using some constant list of byte literals.
#include <cstdint>
class Packet
{
public:
uint8_t b[4];
Packet() = default;
Packet(const Packet& p) = default;
};
Packet p {{1, 2, 3, 4}};
Up to C++17, the class is an aggregate and can be initialized as in instance "p" or using "p = {{...".
With C++20, this is no longer possible because of the default constructors (we need at least the copy constructor).
See the various compiler errors:
==== gcc -std=c++17
==== clang -std=c++17
==== gcc -std=c++20
init.cpp:18:23: error: no matching function for call to ‘Packet::Packet(<brace-enclosed initializer list>)’
18 | Packet p {{1, 2, 3, 4}};
| ^
init.cpp:13:5: note: candidate: ‘constexpr Packet::Packet(const Packet&)’
13 | Packet(const Packet& p) = default;
| ^~~~~~
init.cpp:13:26: note: no known conversion for argument 1 from ‘<brace-enclosed initializer list>’ to ‘const Packet&’
13 | Packet(const Packet& p) = default;
| ~~~~~~~~~~~~~~^
init.cpp:12:5: note: candidate: ‘constexpr Packet::Packet()’
12 | Packet() = default;
| ^~~~~~
init.cpp:12:5: note: candidate expects 0 arguments, 1 provided
==== clang -std=c++20
init.cpp:18:8: error: no matching constructor for initialization of 'Packet'
Packet p {{1, 2, 3, 4}};
^ ~~~~~~~~~~~~~~
init.cpp:13:5: note: candidate constructor not viable: cannot convert initializer list argument to 'const Packet'
Packet(const Packet& p) = default;
^
init.cpp:12:5: note: candidate constructor not viable: requires 0 arguments, but 1 was provided
Packet() = default;
^
1 error generated.
I have tried various ways to declare an additional constructor using a std::initializer_list<uint8_t> parameter without finding the right way to initialize the field "b" from this parameter.
I have seen similar reports on SO and other sites without a working solution in the case of an array field.
Ideally, I would like to find an initialization syntax which works for all levels of standards. If a specific constructor needs to be #ifdef'ed on C++20, this is acceptable. However, there are too many initializations of Packet instances to have distinct syntaxes from C++11 to C++20 (pre-C++11 is not supported by the application).
Any idea? Thanks in advance.
Don't explicitly
defaultthings you don't have to. There is no reason todefaulteither of those constructors; you'll get them either way.It is the presence of an explicitly
defaulted constructor that stops it from being an aggregate. Remove those, and your code works just fine on all C++ versions post-11.