Home > Net >  C 20 modified definition of aggregate, how to initialize instances with constant array of bytes?
C 20 modified definition of aggregate, how to initialize instances with constant array of bytes?

Time:09-01

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.

CodePudding user response:

Don't explicitly default things you don't have to. There is no reason to default either 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.

CodePudding user response:

In case your actual implementation is not defaulting your constructors and you can't use Nicol's answer, you can create an array constructor like this to keep the rest of the code identical:

Packet(const decltype(b)& a)
{
    memcpy(b, a, sizeof a);
}

Working Example

  • Related