C ++: использование оператора двух внутренних типов в качестве объекта функции - PullRequest
5 голосов
/ 16 ноября 2008

У меня есть вектороподобный класс, который содержит массив объектов типа "T", и я хочу реализовать 4 арифметических оператора, которые будут применять операцию к каждому элементу:

// Constructors and other functions are omitted for brevity.
template<class T, unsigned int D>
class Vector {

public:
    // Add a value to each item: naive implementation.
    void operator += (const T&) {
        for (int i = 0; i < D; ++i) {
            data[i] += value;
        }
    }
    void operator -= (const T&) { ... }
    void operator *= (const T&) { ... }
    void operator /= (const T&) { ... }

private:
    T items[D];
};

Поскольку операторы будут содержать один и тот же шаблонный код (зацикливание на каждом элементе и применение соответствующей операции), я подумал, что могу обобщить его:

template<class T, unsigned int D>
class Vector {

public:
    void operator += (const T& value) { do_for_each(???, value); }
    void operator -= (const T& value) { do_for_each(???, value); }
    void operator *= (const T& value) { do_for_each(???, value); }
    void operator /= (const T& value) { do_for_each(???, value); }

private:
    void
    do_for_each(std::binary_function<void, T, T>& op, T value) {
        std::for_each(data, data + D, std::bind2nd(op, value));
    }

    T data[D];
};

Теперь проблема в том, как передать оператор, который принимает два внутренних типа и возвращает от void до do_for_each, как показано в примере выше? C ++ не позволяет мне делать этот трюк для внутренних типов ("T::operator+=" не будет работать, если "T" равен "int").

Ответы [ 3 ]

8 голосов
/ 16 ноября 2008

Во-первых, вы действительно должны вернуть ссылку от вашего оператора + =, поскольку позже вы сможете использовать их для реализации оператора +, оператора- и так далее. Я изменю это соответственно.

Кроме того, ваш do_for_each должен быть шаблоном, поскольку он должен знать точный тип функционального объекта, так как двоичные функциональные объекты не являются полиморфными классами. Для фактической операции вы хотите использовать std::transform:

template<class T, unsigned int D>
class Vector {

public:
    Vector& operator += (const T& value) { 
        do_for_each(std::plus<T>(), value); 
        return *this;
    }

    Vector& operator -= (const T& value) { 
        do_for_each(std::minus<T>(), value); 
        return *this;
    }

    Vector& operator *= (const T& value) { 
        do_for_each(std::multiplies<T>(), value);
        return *this; 
    }

    Vector& operator /= (const T& value) { 
        do_for_each(std::divides<T>(), value); 
        return *this;
    }

private:
    template<typename BinFun>
    void do_for_each(BinFun op, const T& value) {
        std::transform(data, data + D, data, std::bind2nd(op, value));
    }

    T data[D];
};

std :: transform просто передает каждый элемент объекту функции и присваивает результат обратно итератору, заданному в качестве третьего аргумента.

2 голосов
/ 16 ноября 2008

Вы действительно должны взглянуть на Boost Operators , библиотеку только для заголовков, которая действительно упрощает создание ортогональных и согласованных перегрузок арифметических операторов.

В частности: вы можете обнаружить, что наследование от boost::operators::integer_arithmatic<T> спасет вас от повторения этого класса.

1 голос
/ 16 ноября 2008

Я думаю, что LITB на правильном пути и ответил на точный вопрос.
Но я думаю, что это неправильное решение.

Я бы предпочел не использовать do_for_each (), а использовать напрямую std :: transform ():

template<class T, unsigned int D>
class Vector 
{

  public:
    Vector& operator += (const T& value) { 
        std::transform(data, data + D, data, std::bind2nd(std::plus<T>(),value)); 
        return *this;
    }

    Vector& operator -= (const T& value) { 
        std::transform(data, data + D, data, std::bind2nd(std::minus<T>(),value)); 
        return *this;
    }

    Vector& operator *= (const T& value) { 
        std::transform(data, data + D, data, std::bind2nd(std::multiplies<T>(),value));
        return *this; 
    }

    Vector& operator /= (const T& value) { 
        std::transform(data, data + D, data, std::bind2nd(std::divides<T>(),value)); 
        return *this;
    }

  private:
    T data[D];
};
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...