14-05-2024, 06:19 PM
It seems rule 15.0.1 has conflicting requirements for pure virtual / abstract classes with no data members. e.g. if we create the following classes:
Per the rule, we either need
However these prevent us from being able to create a std::unique_ptr to the base class (and allow the derived classes to manage their data through their destructors), or prevents us from being able to copy or move one CanClassicMessage to another CanClassicMessage, if the base copy/move constructors are deleted.
There is an exception for Aggreagate types, but it seems there needs to be an other one for an Interface/Abstract class which holds no data.
Code:
class CanMessage {
public:
CanMessage();
// Required for inheritance
virtual ~CanMessage() = default;
// Want to move and copy through derived classes
CanMessage(const CanMessage&) noexcept = default;
CanMessage(CanMessage&&) noexcept = default;
CanMessage& operator=(const CanMessage&) & noexcept = default;
CanMessage& operator=(CanMessage&&) & noexcept = default;
virtual uint32_t Id() = 0;
virtual uint8_t DataLength() = 0;
virtual std::span<std::byte> Data() = 0;
};
class CanClassicMessage : public CanMessage {
public:
CanClassicMessage(uint32_t id, uint8_t data_length) : CanMessage{}, id_{id} data_length_{data_length} {}
~CanClassicMessage() noexcept override = default;
CanClassicMessage(const CanClassicMessage&) noexcept = default;
CanClassicMessage(CanClassicMessage&&) noexcept = default;
CanClassicMessage& operator=(const CanClassicMessage&) & noexcept = default;
CanClassicMessage& operator=(CanClassicMessage&&) & noexcept = default;
uint32_t Id() override { return id_; }
uint8_t DataLength() override { return data_length_; }
std::span<std::byte> Data() { return data_; }
private:
uint32_t id_;
uint8_t data_length_;
std::array<std::byte, 8> data_;
};
class CanFDMessage : public CanMessage {
public:
CanFDMessage(uint32_t id, uint8_t data_length) : CanMessage{}, id_{id} data_length_{data_length} {}
~CanFDMessage() noexcept override = default;
CanFDMessage(const CanFDMessage&) noexcept = default;
CanFDMessage(CanFDMessage&&) noexcept = default;
CanFDMessage& operator=(const CanFDMessage&) & noexcept = default;
CanFDMessage& operator=(CanFDMessage&&) & noexcept = default;
uint32_t Id() override { return id_; }
uint8_t DataLength() override { return data_length_; }
std::span<std::byte> Data() { return data_; }
private:
uint32_t id_;
uint8_t data_length_;
std::array<std::byte, 64> data_;
}
Per the rule, we either need
- a protected non-virtual destructor.
- Unmovable class that has a public virtual destructor.
However these prevent us from being able to create a std::unique_ptr to the base class (and allow the derived classes to manage their data through their destructors), or prevents us from being able to copy or move one CanClassicMessage to another CanClassicMessage, if the base copy/move constructors are deleted.
There is an exception for Aggreagate types, but it seems there needs to be an other one for an Interface/Abstract class which holds no data.