Операторы перегрузки как функция-член или функция не-член (друга)?

В настоящее время я создаю класс утилиты, в котором будут перегружены операторы. Каковы плюсы и минусы того, что они делают их членами или нечленом (friend)? Или это имеет значение вообще? Может быть, для этого есть лучшая практика?

Ответ 1

У каждого оператора есть свои соображения. Например, < оператор (при использовании для вывода потока, а не сдвиг бита) получает в качестве первого параметра ostream, поэтому он не может быть членом вашего класса. Если вы реализуете оператор добавления, вы, вероятно, захотите воспользоваться автоматическими преобразованиями типов с обеих сторон, поэтому вы также будете иметь не-член и т.д.

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

Ответ 2

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

Одна из причин: она работает лучше с неявным преобразованием типа. Пример. У вас сложный класс с перегруженным оператором *. Если вы хотите написать 2.0 * aComplexNumber, вам нужно, чтобы оператор * был нечленом.

Другая причина: меньше сцепления. Функции, не являющиеся членами, менее тесно связаны, чем функции-члены. Это почти всегда хорошо.

Ответ 3

Если вы выполняете op, то, скорее всего, вам нужно реализовать op =. т.е. если вы перегружаете + оператор, тогда вы должны реализовать + =. Убедитесь, что вы возвращаете const к объекту, если вы выполняете операцию post-increment или overloading +. Итак, если вы перегрузите оператор +, тогда реализуйте его как оператор, не являющийся членом, и используйте внутри него оператор + =. Например,

const A operator+(const A& lhs, const A& rhs)
{
   A ret(lhs);
   ret += rhs;
   return ret;
}

Ответ 4

Если вы планируете внедрять потоковые операторы (< < и → ), то они будут не-членами, потому что ваш объект находится слева от оператора.

Если вы планируете реализовать → ,() или [], они являются, естественно, методами-членами.

Для остальных (сравнение и математика) вы должны проверить Boost.Operators, это действительно помогает.

Например, если вы хотите реализовать следующие операторы:

MyClass& MyClass::operator+=(int);
MyClass operator+(const MyClass&, int);
MyClass operator+(int, const MyClass&);

Вам нужно только написать:

class MyClass: boost::operator::addable<MyClass,int> // no need for public there
{
public:
  MyClass& operator+=(int);
private:
};

2 operator+ будет автоматически генерироваться как нечлены, что позволит вам использовать автоматические преобразования. И они будут эффективно реализованы с точки зрения operator+=, поэтому вы пишете код только один раз.

Ответ 5

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

Рассмотрим простой класс строк:

class str
{
public:
    str(const char *);
    str(const str &other);
};

Если вы реализуете оператор + как функцию-член, а str("1") + "2" будет компилироваться, "1" + str("2") не будет компилироваться.

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

Ответ 6

Нет ничего лучше передового опыта, но это зависит от оператора, которого вы перегружаете.

Например,

  • и < < не могут быть перегружены как функции-члены.

  • Предположим, что вы хотите сделать это следующим образом: obj1 = 2 * obj2, затем перейдите к не-членной функции.

Для функции при перегрузке двоичных операторов функция-член принимает только 1 параметр (вызов объекта передается беспристрастно), тогда как функция нечлена принимает 2 параметра.