Почему нельзя использовать функцию, не являющуюся членом, для перегрузки оператора присваивания? - PullRequest
28 голосов
/ 14 октября 2010

Оператор присваивания может быть перегружен с использованием функции-члена, но не без функции friend функция:

class Test
{
    int a;
public:
    Test(int x)
        :a(x)
    {}
    friend Test& operator=(Test &obj1, Test &obj2);
};

Test& operator=(Test &obj1, Test &obj2)//Not implemented fully. just for test.
{
    return obj1;
}

Это вызывает эту ошибку:

ошибка C2801: оператор = должен быть нестатическим элементом

Почему нельзя использовать функцию friend для перегрузки оператора присваивания? Компилятор позволяет перегружать другие операторы, такие как += и -=, используя friend. В чем заключается проблема / ограничение поддержки operator=?

Ответы [ 9 ]

34 голосов
/ 14 октября 2010

Во-первых, следует отметить, что это не имеет ничего общего с оператором, реализуемым как друг , в частности. На самом деле речь идет о реализации копирования-назначения как функции-члена или как не-членской (автономной) функции. Будет ли эта автономная функция другом или нет, совершенно не имеет значения: это может быть, а может и не быть, в зависимости от того, к чему она хочет получить доступ в классе.

Теперь ответ на этот вопрос дан в книге D & E ( Дизайн и эволюция C ++ ). Причина этого в том, что компилятор всегда объявляет / определяет оператор копирования-присваивания члена для класса (если вы не объявляете свой собственный оператор копирования-присваивания члена).

Если язык также позволяет объявлять оператор копирования-копирования как отдельную (не являющуюся членом) функцию, вы можете получить следующее

// Class definition
class SomeClass {
  // No copy-assignment operator declared here
  // so the compiler declares its own implicitly
  ...
};

SomeClass a, b;

void foo() {
  a = b;
  // The code here will use the compiler-declared copy-assignment for `SomeClass`
  // because it doesn't know anything about any other copy-assignment operators
}

// Your standalone assignment operator
SomeClass& operator =(SomeClass& lhs, const SomeClass& rhs);

void bar() {
  a = b;
  // The code here will use your standalone copy-assigment for `SomeClass`
  // and not the compiler-declared one 
}

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

Это было сочтено неприемлемо опасным (и это так), поэтому C ++ не позволяет объявлять оператор копирования-копирования как отдельную функцию.

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

27 голосов
/ 14 октября 2010

Потому что значение по умолчанию operator=, предоставляемое компилятором (копией по элементам), всегда будет иметь приоритет. То есть твой друг operator= никогда бы не позвонил.

РЕДАКТИРОВАТЬ: Этот ответ отвечает на

Какова внутренняя проблема / ограничение в поддержке = оператора?

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

8 голосов
/ 14 октября 2010

$ 13.5.3 - «Оператор присваивания должен быть реализован нестатической функцией-членом с точно одним параметром. Поскольку оператор оператора копирования = неявно объявлен для класса, если он не объявлен пользователем (12.8 ), оператор присваивания базового класса всегда скрыт оператором присваивания копии производного класса. "

7 голосов
/ 14 октября 2010

Потому что есть некоторые операторы, которые ДОЛЖНЫ быть членами.Это следующие операторы:
operator[]
operator=
operator()
operator->

и операторы преобразования типов, например operator int.

Хотя можно объяснить, почему именно оператор = должен быть членом, их аргумент не может применяться к другим в списке, что заставляет меня поверить, что ответ «Почему» - «Просто потому что».

HTH

3 голосов
/ 14 октября 2010

operator= - это специальная функция-член, которую компилятор предоставит, если вы не объявите ее самостоятельно.Из-за этого особого статуса operator= имеет смысл, что ro требует, чтобы он был функцией-членом, поэтому нет возможности присутствовать как сгенерированный компилятором член operator=, так и объявленный пользователем друг operator=, и нет возможностивыбора между двумя.

1 голос
/ 14 октября 2010

Почему функция Friend не может использоваться для перегрузки оператора присваивания?

Краткий ответ: Только потому, что .

Несколько более длинный ответ: так был исправлен синтаксис.Несколько операторов должны быть функциями-членами .Оператор присваивания является одним из,

0 голосов
/ 28 мая 2015

Поскольку уже существует неявная функция перегрузки операторов для '=' в классе для выполнения поверхностного копирования .Таким образом, даже если вы перегрузите с помощью функции друга , вы никогда не сможете вызвать ее , так как любой сделанный нами вызов вызовет неявный метод поверхностного копирования, а не перегруженную функцию друга.

0 голосов
/ 19 февраля 2015

Это сообщение относится к C ++ 11

Почему кто-то хочет не-член operator=?Ну, с членом operator= тогда возможен следующий код:

Test const &ref = ( Test() = something ); 

, который создает висячую ссылку.Оператор, не являющийся членом, исправит это:

Test& operator=(Test &obj1, Test obj2)

, потому что теперь prvalue Test() не сможет связываться с obj1.Фактически, эта подпись обеспечила бы, чтобы мы никогда не возвращали висячую ссылку (если, конечно, нам не была предоставлена ​​одна) - функция всегда возвращает «правильное» lvalue, потому что она принудительно вызывает вызов с lvalue.в C ++ 11 теперь есть способ указать, что функция-член может вызываться только для lvalues, поэтому вы можете достичь той же цели, написав функцию-член:

Test &operator=(Test obj2) &
//                        ^^^

Теперь приведенный выше код ссвисающая ссылка не будет скомпилирована.


NB.operator= должно занимать правую часть либо по значению, либо по константе.Взятие по значению полезно при реализации идиомы копирования и обмена , техники, позволяющей легко писать безопасные (но не обязательно самые быстрые) операторы копирования-назначения и перемещения-назначения.

0 голосов
/ 04 июля 2012

Назначение operator= является операцией присваивания текущему объекту.Тогда LHS, или lvalue, является объектом того же типа.

Рассмотрим случай, когда LHS - это целое число или некоторый другой тип.Это случай, обрабатываемый operator int() или соответствующей функцией operator T().Следовательно, тип LHS уже определен, но функция, не являющаяся членом operator=, может нарушить это.

Следовательно, этого следует избегать.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...