Подтвердить что ты не робот

Могу ли я создать новый оператор на С++ и как?

Массивы MATLAB поддерживают операции с матрицами и операции с элементами. Например, M*N и M.*N. Это довольно интуитивный способ отличить две разные операции. Если я хочу реализовать аналогичные операции на С++, как я могу это сделать?

Можно ли создать новый оператор, .*, тоже? Если да, может ли кто-нибудь дать мне некоторое руководство?

4b9b3361

Ответ 1

Нет, вы не можете перегрузить op.*:

[C++03 & C++11: 13.5/3]: Следующие операторы не могут быть перегружены:

. .* :: ?:

Ответ 2

Вы не можете перегрузить .* (см. Легкость 'ответ для стандартного текста), но, что интересно, вы можете перегрузить ->* (подобно тому, как вы можете перегрузка ->, но не .). Если этого достаточно для дифференцирования, то на нем:

struct Int {
    int i;

    Int operator*(Int rhs) const { return Int{i * rhs.i}; }
    Int operator->*(Int rhs) const { return Int{i + rhs.i}; }

    friend std::ostream& operator<<(std::ostream& os, Int rhs) {
        return os << "Int(" << rhs.i << ')';
    }
};

int main() {
    Int five{5};
    Int six{6};

    std::cout << (five * six) << ", " << (five ->* six) << '\n';
}

Это напечатает Int(30), Int(11).

Ответ 3

В С++ существует список предопределенных операторов, большинство из которых являются перегружаемыми (. * is not). Кроме того, любое имя может использоваться как оператор, например:

#include <iostream>

// generic LHSlt holder
template<typename LHS, typename OP>
struct LHSlt {
    LHS lhs_;
};

// declare myop as an operator-like construct
enum { myop };

// parse 'lhs <myop' into LHSlt
template<typename LHS>
LHSlt<LHS, decltype(myop)> operator<(const LHS& lhs, decltype(myop))
{
    return { lhs };
}

// declare (int <myop> int) -> int
int operator>(LHSlt<int, decltype(myop)> lhsof, int rhs)
{
    int& lhs = lhsof.lhs_;
    // here comes your actual implementation
    return (lhs + rhs) * (lhs - rhs);
}

// strictly optional
#define MYOP <myop>

int main() {
    std::cout << (5 <myop> 2) << ' ' << (5 MYOP 2);
}

Отказ от ответственности: Это, строго говоря, переводится на (5 < myop) > 2, что составляет LHSlt<int, decltype(myop)>(5) > 2. Таким образом, это не новый "оператор" в терминах С++, но он используется точно так же, даже с точки зрения ADL. Кроме того, если тип большой, вы, вероятно, захотите сохранить const T&.

Обратите внимание, что вы можете сделать это с любым двоичным оператором, который может быть определен внешним по отношению к классу; приоритет основан на приоритете двух сторон (< и >). Таким образом, вы можете иметь, например, *myop*, +myop+, <<myop>>, <myop>, |myop| в этом порядке приоритета.

Если вам нужна правильная ассоциативность, она становится немного сложнее. Вам понадобится как держатель RHS, так и держатель LHS (последний здесь LHSlt здесь) и использовать окружающие операторы, так что правый имеет более высокий приоритет, чем левый, например. a |myop> b |myop>c - a |myop> (b |myop> c). Затем вам нужна функция как для вашего типа, так и для вашего типа держателя как lhs.

Ответ 4

Нет, к сожалению, вы не можете определить новые операторы - вы можете только перегружать существующие операторы (с несколькими важными исключениями, например operator.). Даже тогда, как правило, просто рекомендуется перегрузить операторы для типов, которые имеют очень четкую и бесспорную существующую семантику для данного оператора - например, любой тип, который ведет себя как число, является хорошим кандидатом для перегрузки операторов арифметики и сравнения, но вы должны убедиться, что operator+, например, не вычитает два числа.

Ответ 5

Массивы MATLAB поддерживают операции с матрицами и операции с элементами. Например, M * N и M. * N. Это довольно интуитивный способ отличить две разные операции. Если я хочу реализовать аналогичные операции на С++, как я могу это сделать?

Можно ли создать новый оператор, *, тоже? Если да, может ли кто-нибудь дать мне некоторое руководство?

Что касается первой части, вы можете перегрузить большинство операторов, и есть некоторые, которые вы не можете перегрузить, а список операторов на С++:

  • Арифметика

    • + (addition)
    • - (subtraction)
    • * (multiplication)
    • / (division)
    • % (modulus)
  • побитовое

    • ^ (XOR)
    • | (OR)
    • & (AND)
    • ~ (Complement)
    • << (Shift Left, Insertion to Stream)
    • >> (Shift Right, Extraction from Stream)
  • Назначение

    • = (Assignment)
  • Реляционная

    • == (Equality)
    • != (Inequality)
    • > (Greater-Than)
    • < (Less-Than)
    • >= (Greater-Than Or Equal-To)
    • <= (Less-Than Or Equal-To)
  • Логическое

    • ! (NOT)
    • && (AND)
    • || (OR)
  • Соединение Назначение

    • += (Addition-Assignment)
    • -= (Subtraction-Assignment)
    • *= (Multiplication-Assignment)
    • /= (Division-Assignment)
    • %= (Modulus-Assignment)
    • &= (AND-Assignment)
    • |= (OR-Assignment)
    • ^= (XOR-Assignment)
    • <<= (Shift-Left Assignment)
    • >>= (Shift-Right Assignment)
  • Increment-Decrement - оба имеют 2 формы (префикс) и (постфикс)

    • ++ (Increment)
    • -- (Decrement)
  • подстрочный

    • [] (Subscript)
  • Вызов функции

    • () (Function Call)
  • Адрес, ссылка, указатель

    • operator&()
    • operator*()
    • operator->()
  • Comma

    • operator,()
  • Ссылка участника

    • operator->()
    • operator->*()
  • Управление памятью

    • new
    • delete
    • new[]
    • delete[]
  • Конверсия

    • operator "type" () const
  • NON Modifiable Operators - Операторы, которые нельзя перегрузить

    • ?: (Conditional - Ternary)
    • . (Member Selection)
    • .* (Member Selection With Pointer To Member)
    • :: (Scope Resolution)
    • sizeof() (Object Size Information)
    • typeid() (Object Type Information)

Знание этого списка поможет ответить на ваши вопросы. Можете ли вы создать "Новый оператор" на С++? Нет! Если вы хотите реализовать аналогичные операции в С++; как я могу это сделать?

У вас есть 4 варианта: либо перегрузите уже существующий оператор, который может быть перегружен, напишите функцию или метод, чтобы выполнить тип вычислений, которые вы хотите выполнить, создайте тип шаблона, чтобы выполнить работу за вас, или последний который является наименее распространенным, но вы также можете написать макросы, чтобы сделать их для вас.

Существует только библиотека заголовков Math API, которая используется довольно часто с графическим интерфейсом OpenGL и OpenGL Shader Language GLSL, и эта библиотека имеет множество функций, которые работают с векторами, матрицами, кватернионами и т.д. и всеми необходимыми функциями и операциями, которые могут быть сделаны с ними. Вот ссылка на GLM Вы можете посмотреть их документацию, а также их реализацию библиотек, поскольку она является только библиотекой заголовков или API. Это должно дать вам некоторое представление о том, как они построили объекты Vector и Matrix и операции, которые могут быть выполнены с ними.

Ответ 6

Кстати: я пытаюсь ответить на части этого вопроса, как было задано. Я также не пытаюсь воспроизвести всю информацию в других достойных ответах. Щедрость ищет что-то другое в вопросе, так как я не отвечаю на это.

На самом деле довольно просто обеспечить матричное умножение. Поскольку я не предлагаю описывать структуры данных для представления матрицы и полностью выполнять операции и проверки действительности на них, я просто предоставил скелеты для иллюстрации.

Пример 1: operator*() как функция-член

class M   // a basic matrix class
{
    public:

          // assume other constructors and members to set things up
       M operator*(const M &rhs) const;
};

M M::operator*(const M &rhs) const
{
       //   implement checks on dimensions, throw an exception if invalid

       M result;
        //  implement the multiplication (typical iterations) and store results in result
       return result;
}

int main()
{
     M a;
     M b;
        // set up elements of a and b as needed
     M c = a*b;    // this relies on M having appropriate constructor(s) to copy or move the result of a*b into c

     M d;
     d = a * b;    //  this relies on M having appropriate operator=() to assign d to the result of a*b
}

Вышеупомянутое реализует operator*() как функцию-член. Таким образом, функционально c = a*b эквивалентно c = a.operator*(b). Квалификаторы const представляют тот факт, что матричное умножение a*b обычно не изменяется a или b.

Пример 2: operator*() как функция, не являющаяся членом

Теперь operator*() также может быть реализована как не-член (необязательно a friend) со скелетом, который выглядит как

class M   // our basic matrix class, different operator *
{
    public:

          // assume other constructors and members to set things up
       friend M operator*(const M &lhs, const M &rhs);
};

M operator*(const M &lhs, const M &rhs)
{
       //   implement checks on dimensions, throw an exception if invalid

       M result;
        //  implement the multiplication (typical iterations) and store results in result
       return result;
}

//   same main() as before

Заметим, что в этом случае a*b теперь эквивалентно operator*(a, b).

Если вы хотите использовать обе формы, необходимо соблюдать осторожность, чтобы избежать двусмысленности. Если указаны обе формы operator*(), они являются действительными совпадениями в выражении типа c = a*b, и компилятор не имеет возможности выбирать одну форму над другой. В результате код не компилируется.

Пример 3: перегрузка operator*()

Также возможно перегрузить operator*() - например, для умножения матрицы на скаляр.

class M   // a basic matrix class
{
    public:

          // assume other constructors and members to set things up
       M operator*(const M &rhs) const;    // as in first example

       M operator*(double scalar) const;    // member form
       friend M operator*(double scalar, const M &rhs);   // non-member form
};

M M::operator*(double scalar) const
{
       M result;
        //  implement the multiplication (typical iterations) and store results in result
       return result;
}

M operator*(double scalar, const M &m)
{
       M result;
        //  implement the multiplication (typical iterations) and store results in result
       return result;
}

int main()
{
     M a;
     M b;
        // set up elements of a and b as needed
     M c = b * 2.0;    // uses the member form of operator*() above

     M d;
     d = 2.0*a;        //  uses the non-member form of operator*() above
}

В приведенном выше выражении b*2.0 соответствует вызову b.operator*(2.0) и 2.0*a для вызова нечлена operator*(2.0, a). Формы-члены могут использоваться только в выражениях, где левый операнд имеет тип M. Поэтому 2.0*a не будет работать, если предусмотрены только формы-члены operator*().

Обсуждение

Помимо проблем двусмысленности выше, есть и другие вещи, о которых следует помнить при перегрузке операторов.

  • Невозможно изменить приоритет или ассоциативность операторов по их спецификации в языковых правилах. Итак, в выражении a+b*c * всегда будет иметь более высокий приоритет, чем +. Это также является причиной того, что не рекомендуется перегружать ^ для возведения в степень в С++, так как ^ имеет более низкий приоритет, чем + в С++ (это побитовая операция на интегральных типах). Итак, a + b^c на самом деле эквивалентен в С++ до (a + b)^c, а не a + (b^c) (который ожидал бы любой, у кого было бы базовое знание алгебры).
  • Язык определяет набор операторов, и невозможно создать новые. Например, в С++ нет **, так что a ** b повышает a до степени b (что могут делать другие языки), и его невозможно создать.
  • Не все операторы могут быть перегружены.

Один из операторов, которые нельзя перегрузить в С++, .*. Таким образом, невозможно использовать такой оператор, как в Matlab. Обычно я предлагаю НЕ пытаться получить тот же эффект с помощью других операторов, потому что вышеупомянутые ограничения будут влиять на это (и вызывать выражения, дающие контр-интуитивное поведение). Вместо этого просто предоставляйте другую именованную функцию для выполнения задания. Например, в качестве функции-члена

   class M
   {
       public:
         // other stuff

          M ElementWiseProduct(const M &) const;
   };

Ответ 7

Большинство ответов уже рассмотрели, какие операторы являются и не являются перегружаемыми, но ни один из них не обсуждал, почему некоторые являются изменяемыми, а некоторые нет.

Ниже приведена цитата из Bjarne Stroustrup (парень, который написал С++), который я нашел в fooobar.com/questions/83326/.... Обратите особое внимание на третий параграф.

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

В то время я считал следующие аргументы убедительными: если obj - объект класса, то obj.m имеет значение для каждого члена m этого класса объекта. Мы стараемся не изменять язык путем переопределения встроенных операций (хотя это правило нарушено для = из-за крайней необходимости и для унарного &).

Если мы допустили перегрузку. для класса X мы не смогли бы получить доступ к элементам X обычными способами; мы должны были бы использовать указатель и → , но → , а также могли бы быть переопределены. Мне нужен расширяемый язык, а не изменчивый.

Эти аргументы веские, но не окончательные. В частности, в 1990 году Джим Адкок предложил разрешить перегрузку оператора. точно так же, как оператор → .

Страница на его сайте добавляет немного больше:

Могу ли я определить свои собственные операторы?

Прости, нет. Возможность рассматривалась несколько раз, но каждый раз, когда мы/мы решили, что вероятные проблемы перевешивают вероятные выгоды.

Это не проблема языка. Даже когда я впервые рассмотрел его в 1983 году, я знал, как это можно реализовать. Тем не менее, мой опыт заключается в том, что, когда мы выходим за пределы самых тривиальных примеров, люди, кажется, имеют тонко разные мнения о "очевидном" смысле использования оператора. Классическим примером является ** b ** c. Предположим, что ** было сделано для обозначения возведения в степень. Теперь следует ли ** ** ** с означать (а ** Ь) ** с или ** (Ь ** с)? Я думал, что ответ очевиден, и мои друзья согласились - и тогда мы обнаружили, что мы не согласны с тем, какая резолюция была очевидной. Моя гипотеза заключается в том, что такие проблемы приведут к тонким ошибкам.

Таким образом, хотя большинство операторов могут быть перегружены, никогда не предназначалось для людей создавать произвольные операторы в С++.

Ответ 8

Это так же просто (и как сложно!) как определение функции с именем (в данном случае) operator*():

Matrix operator*(const Matrix &m1, const Matrix &m2) ...

где Matrix - это класс, который вы определили для представления матриц.

Ответ 9

Как говорят другие ответы, перегрузка operator.* невозможна.

Но у меня есть хорошее решение для вашего вопроса, проверьте здесь.

Вы можете предоставить любые методы в форме оператор-иш, например:

M <matrix_mul> N