Неправильная перегрузка оператора, CRTP, C++

Я использую CRTP для перегрузки оператора. У меня есть базовая структура.

template<int S, typename T, typename C>
struct ColorModel {
    constexpr static int SPACE = S;

    ColorModel() {
        std::fill(begin(), end(), 0);
    }

    ColorModel(const ColorModel& another) {
        std::copy(another.begin(), another.end(), begin());
    }

    ColorModel(ColorModel&& another) noexcept {
        std::move(another.begin(), another.end(), begin());
    }

    ColorModel(const std::initializer_list<T>& l) {
        std::copy(l.begin(), l.end(), begin());
    }

    explicit ColorModel(const T& elem) {
        std::fill(begin(), end(), elem);
    }

    constexpr inline const T* cbegin() const {
        return static_cast<const C*>(this)->components.cbegin();
    }

    constexpr inline const T* cend() const {
        return static_cast<const C*>(this)->components.cend();
    }

    constexpr inline const T* begin() const {
        return cbegin();
    }

    constexpr inline const T* end() const {
        return cend();
    }

    constexpr inline T* begin() {
        return static_cast<C*>(this)->components.begin();
    }

    constexpr inline T* end() {
        return static_cast<C*>(this)->components.end();
    }

    constexpr inline size_t size() const {
        return std::distance(begin(), end());
    }

    constexpr inline ColorModel& operator=(const ColorModel& rhs) {
        std::copy(rhs.begin(), rhs.end(), begin());
        return *this;
    }

    constexpr inline ColorModel& operator=(ColorModel&& rhs) noexcept {
        std::move(rhs.begin(), rhs.end(), begin());
        return *this;
    }

    constexpr inline ColorModel& operator=(const T& rhs) {
        std::fill(begin(), end(), rhs);
        return *this;
    }

    constexpr inline ColorModel& operator=(T&& rhs) {
        std::fill(begin(), end(), rhs);
        return *this;
    }

    constexpr inline ColorModel& operator+=(const ColorModel& rhs) {
        std::transform(begin(), end(), rhs.begin(), begin(), std::plus<>());
        return *this;
    }

    constexpr inline ColorModel& operator+=(ColorModel&& rhs) {
        std::transform(begin(), end(), rhs.begin(), begin(), std::plus<>());
        return *this;
    }

    constexpr inline ColorModel& operator+=(const T& rhs) {
        util::transform(begin(), end(), rhs, begin(), std::plus<>());
        return *this;
    }

    constexpr inline ColorModel& operator+=(T&& rhs) {
        util::transform(begin(), end(), rhs, begin(), std::plus<>());
        return *this;
    }

    constexpr inline ColorModel& operator-=(const ColorModel& rhs) {
        std::transform(begin(), end(), rhs.begin(), begin(), std::minus<>());
        return *this;
    }

    constexpr inline ColorModel& operator-=(ColorModel&& rhs) {
        std::transform(begin(), end(), rhs.begin(), begin(), std::minus<>());
        return *this;
    }

    constexpr inline ColorModel& operator-=(const T& rhs) {
        util::transform(begin(), end(), rhs, begin(), std::minus<>());
        return *this;
    }

    constexpr inline ColorModel& operator-=(T&& rhs) {
        util::transform(begin(), end(), rhs, begin(), std::minus<>());
        return *this;
    }

    constexpr inline ColorModel& operator*=(const ColorModel& rhs) {
        std::transform(begin(), end(), rhs.begin(), begin(), std::multiplies<>());
        return *this;
    }

    constexpr inline ColorModel& operator*=(ColorModel&& rhs) {
        std::transform(begin(), end(), rhs.begin(), begin(), std::multiplies<>());
        return *this;
    }

    constexpr inline ColorModel& operator*=(const T& rhs) {
        util::transform(begin(), end(), rhs, begin(), std::multiplies<>());
        return *this;
    }

    constexpr inline ColorModel& operator*=(T&& rhs) {
        util::transform(begin(), end(), rhs, begin(), std::multiplies<>());
        return *this;
    }

    constexpr inline ColorModel& operator/=(const ColorModel& rhs) {
        std::transform(begin(), end(), rhs.begin(), begin(), std::divides<>());
        return *this;
    }

    constexpr inline ColorModel& operator/=(ColorModel&& rhs) {
        std::transform(begin(), end(), rhs.begin(), begin(), std::divides<>());
        return *this;
    }

    constexpr inline ColorModel& operator/=(const T& rhs) {
        util::transform(begin(), end(), rhs, begin(), std::divides<>());
        return *this;
    }

    constexpr inline ColorModel& operator/=(T&& rhs) {
        util::transform(begin(), end(), rhs, begin(), std::divides<>());
        return *this;
    }

    constexpr inline ColorModel operator+(const ColorModel& rhs) const {
        ColorModel result;
        std::transform(begin(), end(), rhs.begin(), result.begin(), std::plus<>());
        return result;
    }

    constexpr inline ColorModel operator+(ColorModel&& rhs) const {
        ColorModel result;
        std::transform(begin(), end(), rhs.begin(), result.begin(), std::plus<>());
        return result;
    }

    constexpr inline ColorModel operator+(const T& rhs) const {
        ColorModel result;
        util::transform(begin(), end(), rhs, result.begin(), std::plus<>());
        return result;
    }

    constexpr inline ColorModel operator+(T&& rhs) const {
        ColorModel result;
        util::transform(begin(), end(), rhs, result.begin(), std::plus<>());
        return result;
    }

    constexpr inline ColorModel operator-(const ColorModel& rhs) const {
        ColorModel result;
        std::transform(begin(), end(), rhs.begin(), result.begin(), std::minus<>());
        return result;
    }

    constexpr inline ColorModel operator-(ColorModel&& rhs) const {
        ColorModel result;
        std::transform(begin(), end(), rhs.begin(), result.begin(), std::minus<>());
        return result;
    }

    constexpr inline ColorModel operator-(const T& rhs) const {
        ColorModel result;
        util::transform(begin(), end(), rhs, result.begin(), std::minus<>());
        return result;
    }

    constexpr inline ColorModel operator-(T&& rhs) const {
        ColorModel result;
        util::transform(begin(), end(), rhs, result.begin(), std::minus<>());
        return result;
    }

    constexpr inline ColorModel operator*(const ColorModel& rhs) const {
        ColorModel result;
        std::transform(begin(), end(), rhs.begin(), result.begin(), std::multiplies<>());
        return result;
    }

    constexpr inline ColorModel operator*(ColorModel&& rhs) const {
        ColorModel result;
        std::transform(begin(), end(), rhs.begin(), result.begin(), std::multiplies<>());
        return result;
    }

    constexpr inline ColorModel operator*(const T& rhs) const {
        ColorModel result;
        util::transform(begin(), end(), rhs, result.begin(), std::multiplies<>());
        return result;
    }

    constexpr inline ColorModel operator*(T&& rhs) const {
        ColorModel result;
        util::transform(begin(), end(), rhs, result.begin(), std::multiplies<>());
        return result;
    }

    constexpr inline ColorModel operator/(const ColorModel& rhs) const {
        ColorModel result;
        std::transform(begin(), end(), rhs.begin(), result.begin(), std::divides<>());
        return result;
    }

    constexpr inline ColorModel operator/(ColorModel&& rhs) const {
        ColorModel result;
        std::transform(begin(), end(), rhs.begin(), result.begin(), std::divides<>());
        return result;
    }

    constexpr inline ColorModel operator/(const T& rhs) const {
        ColorModel result;
        util::transform(begin(), end(), rhs, result.begin(), std::divides<>());
        return result;
    }

    constexpr inline ColorModel operator/(T&& rhs) const {
        ColorModel result;
        util::transform(begin(), end(), rhs, result.begin(), std::divides<>());
        return result;
    }
};

template<int S, typename T, typename C>
std::ostream& operator<<(std::ostream& os, const ColorModel<S, T, C>& model) {
    for (const auto& elem : model) {
        os << elem << " ";
    }
    return os;
}

А это потомок базовой структуры

struct RGB : ColorModel<0, int, RGB> {
    std::array<int, 3> components;

    RGB() : ColorModel() {

    }

    RGB(const RGB& other) : ColorModel(other) {

    }

    RGB(RGB&& other) : ColorModel(other) {

    }

    RGB(const std::initializer_list<int>& l) : ColorModel(l) {

    }

    explicit RGB(const int& elem) : ColorModel(elem) {

    }

    using ColorModel<0, int, RGB>::operator=;
};

Вот код моей функции util::transform

namespace util {
    template<typename Iterator, typename T, typename BinaryOperator>
    void transform(Iterator begin, Iterator end, const T& elem, Iterator result, BinaryOperator anOperator) {
        for (; begin != end; ++begin, ++result) {
            *result = anOperator(*begin, elem);
        }
    }
}

Но когда я начал тестировать его в main.cpp, я получил странные результаты.

int main() {
    RGB rgb1{1, 2, 3};
    RGB rgb2{2, 3, 4};

    RGB rgb3(10);
    rgb3 += rgb3 + rgb1+rgb2;
    std::cout << rgb3 << std::endl << rgb1 << std::endl << rgb2;
}

Я получаю 10 15 20 вместо 13 15 17, и я застрял, пытаясь выяснить причину. Спасибо заранее.

P.S. Извините за все встроенные constexpr, я просто тестировал что-то локально.


person Hrant Nurijanyan    schedule 04.12.2020    source источник
comment
@NathanOliver Да, я забыл обновить эту скопированную часть, спасибо)   -  person Hrant Nurijanyan    schedule 04.12.2020
comment
Вы действительно должны протестировать MCVE, который вы нам предоставили, чтобы убедиться, что проблема по-прежнему воспроизводима.   -  person Asteroids With Wings    schedule 04.12.2020
comment
Что такое util? У вас там свои функции?   -  person cigien    schedule 04.12.2020
comment
Извините все, это моя функция, и это не связано с вопросами, я обновил пост и удалил его.   -  person Hrant Nurijanyan    schedule 04.12.2020
comment
Не удается воспроизвести   -  person NathanOliver    schedule 04.12.2020
comment
@NathanOliver Я обновлю вопрос, указав точный код, который у меня есть, больше никаких абстракций имен.   -  person Hrant Nurijanyan    schedule 04.12.2020
comment
Когда различные операторы return result; из локальной переменной базового класса, разве это не срез? Может я что-то упускаю, я мало делаю CRTP.   -  person Eljay    schedule 04.12.2020
comment
У меня сложилось впечатление, что доступ/инициализация членов производного класса до их создания (в базовом классе Ctor) является UB. Может RGB() : components{}, ColorModel() {} ?   -  person Kostas    schedule 04.12.2020


Ответы (2)


Ваша (основная) проблема заключается в том, что вы (не) инициализируете component после того, как базовый класс (ColorModel) установил для него некоторые значения:

У вас может быть постоянная ошибка с явной инициализацией компонента:

explicit RGB(const int& elem) : ColorModel(elem), component{} {}

Демо

Вы можете использовать основание на идиоме участника, чтобы решить эту проблему. .

struct Data
{
    std::array<int, 3> components{};
};

struct RGB : Data, ColorModel<0, int, RGB> {
// ...
};

Демо

У вас также есть проблема с вашим operator+, поскольку ColorModel result; недействителен (там нет производного класса), он должен быть C result; (то же самое с типом возврата).

person Jarod42    schedule 04.12.2020
comment
Я обновил код, как вы сказали, с небольшими изменениями, скоро выложу ответ, спасибо) - person Hrant Nurijanyan; 05.12.2020

Как упомянул @Jarod42, мне нужно было обновить структуру базового цветового пространства для моих нужд. Я сделал следующие обновления.

template<int S, typename T, typename C>
struct ColorModel {

    constexpr inline const T* cbegin() const {
        return static_cast<const C*>(this)->components.cbegin();
    }

    constexpr inline const T* cend() const {
        return static_cast<const C*>(this)->components.cend();
    }

    constexpr inline const T* begin() const {
        return cbegin();
    }

    constexpr inline const T* end() const {
        return cend();
    }

    constexpr inline T* begin() {
        return static_cast<C*>(this)->components.begin();
    }

    constexpr inline T* end() {
        return static_cast<C*>(this)->components.end();
    }

    constexpr inline ColorModel& operator=(const C& rhs) {
        std::copy(rhs.begin(), rhs.end(), begin());
        return *this;
    }

    constexpr inline ColorModel& operator=(C&& rhs) {
        std::move(rhs.begin(), rhs.end(), begin());
        return *this;
    }

    constexpr inline ColorModel& operator=(const T& rhs) {
        std::fill(begin(), end(), rhs);
        return *this;
    }

    constexpr inline ColorModel& operator=(T&& rhs) {
        std::fill(begin(), end(), rhs);
        return *this;
    }

    constexpr inline ColorModel& operator+=(const C& rhs) {
        std::transform(begin(), end(), rhs.begin(), begin(), std::plus<>());
        return *this;
    }

    constexpr inline ColorModel& operator+=(C&& rhs) {
        std::transform(begin(), end(), rhs.begin(), begin(), std::plus<>());
        return *this;
    }

    constexpr inline ColorModel& operator+=(const T& rhs) {
        util::transform(begin(), end(), rhs, begin(), std::plus<>());
        return *this;
    }

    constexpr inline ColorModel& operator+=(T&& rhs) {
        util::transform(begin(), end(), rhs, begin(), std::plus<>());
        return *this;
    }

    constexpr inline ColorModel& operator-=(const C& rhs) {
        std::transform(begin(), end(), rhs.begin(), begin(), std::minus<>());
        return *this;
    }

    constexpr inline ColorModel& operator-=(C&& rhs) {
        std::transform(begin(), end(), rhs.begin(), begin(), std::minus<>());
        return *this;
    }

    constexpr inline ColorModel& operator-=(const T& rhs) {
        util::transform(begin(), end(), rhs, begin(), std::minus<>());
        return *this;
    }

    constexpr inline ColorModel& operator-=(T&& rhs) {
        util::transform(begin(), end(), rhs, begin(), std::minus<>());
        return *this;
    }

    constexpr inline ColorModel& operator*=(const C& rhs) {
        std::transform(begin(), end(), rhs.begin(), begin(), std::multiplies<>());
        return *this;
    }

    constexpr inline ColorModel& operator*=(C&& rhs) {
        std::transform(begin(), end(), rhs.begin(), begin(), std::multiplies<>());
        return *this;
    }

    constexpr inline ColorModel& operator*=(const T& rhs) {
        util::transform(begin(), end(), rhs, begin(), std::multiplies<>());
        return *this;
    }

    constexpr inline ColorModel& operator*=(T&& rhs) {
        util::transform(begin(), end(), rhs, begin(), std::multiplies<>());
        return *this;
    }

    constexpr inline ColorModel& operator/=(const C& rhs) {
        std::transform(begin(), end(), rhs.begin(), begin(), std::divides<>());
        return *this;
    }

    constexpr inline ColorModel& operator/=(C&& rhs) {
        std::transform(begin(), end(), rhs.begin(), begin(), std::divides<>());
        return *this;
    }

    constexpr inline ColorModel& operator/=(const T& rhs) {
        util::transform(begin(), end(), rhs, begin(), std::divides<>());
        return *this;
    }

    constexpr inline ColorModel& operator/=(T&& rhs) {
        util::transform(begin(), end(), rhs, begin(), std::divides<>());
        return *this;
    }

    constexpr inline C operator+(const C& rhs) const {
        C res;
        std::transform(begin(), end(), rhs.begin(), res.begin(), std::plus<>());
        return res;
    }

    constexpr inline C operator+(C&& rhs) const {
        C res;
        std::transform(begin(), end(), rhs.begin(), res.begin(), std::plus<>());
        return res;
    }

    constexpr inline C operator+(const T& rhs) const {
        C res;
        util::transform(begin(), end(), rhs, res.begin(), std::plus<>());
        return res;
    }

    constexpr inline C operator+(T&& rhs) const {
        C res;
        util::transform(begin(), end(), rhs, res.begin(), std::plus<>());
        return res;
    }

    constexpr inline C operator-(const C& rhs) const {
        C res;
        std::transform(begin(), end(), rhs.begin(), res.begin(), std::minus<>());
        return res;
    }

    constexpr inline C operator-(C&& rhs) const {
        C res;
        std::transform(begin(), end(), rhs.begin(), res.begin(), std::minus<>());
        return res;
    }

    constexpr inline C operator-(const T& rhs) const {
        C res;
        util::transform(begin(), end(), rhs, res.begin(), std::minus<>());
        return res;
    }

    constexpr inline C operator-(T&& rhs) const {
        C res;
        util::transform(begin(), end(), rhs, res.begin(), std::minus<>());
        return res;
    }

    constexpr inline C operator*(const C& rhs) const {
        C res;
        std::transform(begin(), end(), rhs.begin(), res.begin(), std::multiplies<>());
        return res;
    }

    constexpr inline C operator*(C&& rhs) const {
        C res;
        std::transform(begin(), end(), rhs.begin(), res.begin(), std::multiplies<>());
        return res;
    }

    constexpr inline C operator*(const T& rhs) const {
        C res;
        util::transform(begin(), end(), rhs, res.begin(), std::multiplies<>());
        return res;
    }

    constexpr inline C operator*(T&& rhs) const {
        C res;
        util::transform(begin(), end(), rhs, res.begin(), std::multiplies<>());
        return res;
    }

    constexpr inline C operator/(const C& rhs) const {
        C res;
        std::transform(begin(), end(), rhs.begin(), res.begin(), std::divides<>());
        return res;
    }

    constexpr inline C operator/(C&& rhs) const {
        C res;
        std::transform(begin(), end(), rhs.begin(), res.begin(), std::divides<>());
        return res;
    }

    constexpr inline C operator/(const T& rhs) const {
        C res;
        util::transform(begin(), end(), rhs, res.begin(), std::divides<>());
        return res;
    }

    constexpr inline C operator/(T&& rhs) const {
        C res;
        util::transform(begin(), end(), rhs, res.begin(), std::divides<>());
        return res;
    }
};

template<int S, typename T, typename C>
std::ostream& operator<<(std::ostream& os, const ColorModel<S, T, C>& model) {
    std::for_each(model.begin(), model.end(), [&](const T& elem) { os << elem << " "; });
    return os;
}

Как видите, я удалил все конструкторы из базового класса, так как это в основном для перегрузки операторов, и позволил дочерней структуре инициализировать свои собственные поля с помощью свой путь. Итак, мой struct RGB был обновлен следующим образом

struct RGB : color_space::ColorModel<0, int, RGB> {
    std::array<int, 3> components{};

    RGB() = default;

    RGB(const std::initializer_list<int>& l){
        std::copy(l.begin(),l.end(),components.begin());
    }

    // All constructors you want

    using color_space::ColorModel<0, int, RGB>::operator=;
};

Это решило проблему с не инициализацией моего std::array<int,3> components. Также мне пришлось изменить реализацию всех перегрузок операторов, где новый экземпляр C( Дочерний элемент) создается. И соответственно измените возвращаемый тип. Спасибо.

person Hrant Nurijanyan    schedule 04.12.2020