Почему не может использоваться функция, не являющаяся членом, для перегрузки оператора присваивания?
Оператор присваивания может быть перегружен с использованием функции-члена, но не является функцией не-члена 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: 'operator =' должен быть нестационарным членом
Почему не может использоваться функция friend
для перегрузки оператора присваивания? Компилятор позволяет перегрузить другие операторы, такие как +=
и -=
, используя friend
. Какая внутренняя проблема/ограничение в поддержке operator=
?
Ответы
Ответ 1
Поскольку значение по умолчанию operator=
, предоставленное компилятором (по порядку), всегда будет иметь приоритет. То есть ваш друг operator=
никогда не будет вызван.
EDIT: этот ответ отвечает на
Какова присущая проблема/ограничение в поддержке = operator?
часть вопроса. Другие ответы здесь приводят часть стандарта, в котором говорится, что вы не можете этого сделать, но это, скорее всего, почему эта часть стандарта была написана именно так.
Ответ 2
Во-первых, следует отметить, что это не имеет никакого отношения к тому, что оператор реализуется как друг специально. Речь идет о реализации назначения копирования как функции-члена или как функции, не являющейся членом (автономной). Независимо от того, будет ли эта автономная функция быть другом или нет, это совершенно не имеет значения: возможно, это может быть не так, в зависимости от того, что она хочет получить в классе.
Теперь ответ на этот вопрос дан в книге D & E (Дизайн и эволюция С++). Причиной этого является то, что компилятор всегда объявляет/определяет оператор-экземпляр-экземпляр для класса (если вы не объявляете свой оператор-экземпляр-экземпляр-член).
Если язык также разрешил объявлять оператор присваивания копии как автономной (нечленой) функции, вы можете получить следующее
// 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
}
Как видно из вышеприведенного примера, семантика присваивания копий будет меняться в середине единицы перевода - перед объявлением вашего автономного оператора используется версия компилятора. После объявления используется ваша версия. Поведение программы изменится в зависимости от того, где вы поместили объявление своего автономного оператора присваивания копий.
Это считалось неприемлемо опасным (и это так), поэтому С++ не позволяет объявлять оператор присваивания копии как автономную функцию.
Верно, что в вашем конкретном примере, который используется специально для функции друга, оператор объявляется очень рано, внутри определения класса (так как это объявляется друзьями). Итак, в вашем случае компилятор, конечно, сразу узнает о существовании вашего оператора. Однако, с точки зрения языка С++, общая проблема никак не связана с функциями друзей. С точки зрения языка С++ речь идет о функциях-членах и не-членных функциях, а перегрузка не-членной перераспределения просто запрещена по причинам, описанным выше.
Ответ 3
$13.5.3 - "Оператор присваивания должен быть реализован нестатической функцией-членом только с одним параметром. Поскольку оператор-оператор присваивания экземпляра = неявно объявлен для класса, если он не объявлен пользователем (12.8), оператор присваивания базового класса всегда скрыт оператором присваивания копии производного класса.
Ответ 4
Потому что есть некоторые операторы, которые ДОЛЖНЫ быть членами. Этими операторами являются:
operator[]
operator=
operator()
operator->
и операторов преобразования типов, например operator int
.
Хотя можно было бы объяснить, почему именно operator = должен быть членом, их аргумент не может применяться к другим в списке, что заставляет меня поверить, что ответ "Почему" есть "Только потому".
НТН
Ответ 5
operator=
- это специальная функция-член, которую компилятор предоставит, если вы не объявите ее самостоятельно. Из-за этого особого статуса operator=
он имеет смысл ro требует, чтобы он был функцией-членом, поэтому нет возможности наличия как созданного компилятором члена operator=
, так и объявленного пользователем друга operator=
и без возможности выбора между ними.
Ответ 6
Почему функция friend не может использоваться для перегрузки оператора присваивания?
Короткий ответ: Только потому, что.
Несколько длиннее ответ: То, как синтаксис был исправлен. Несколько операторов должны быть функциями-членами. Оператор присваивания является одним из <
Ответ 7
Цель operator=
- операция присваивания текущему объекту. Тогда LHS, или lvalue, является объектом того же типа.
Рассмотрим случай, когда LHS является целым или каким-либо другим типом. Это случай, обрабатываемый operator int()
или соответствующей функцией operator T()
. Следовательно, тип LHS уже определен, но функция non-member operator=
может нарушить это.
Следовательно, этого избежать.
Ответ 8
Это сообщение относится к С++ 11
Почему кому-то нужен не-член operator=
? Ну, с членом operator=
, тогда возможен следующий код:
Test const &ref = ( Test() = something );
который создает зависающую ссылку. Оператор, не являющийся членом, зафиксировал бы это:
Test& operator=(Test &obj1, Test obj2)
потому что теперь prvalue Test()
не сможет привязываться к obj1
. На самом деле эта подпись обеспечит, чтобы мы никогда не возвращали оборванную ссылку (если бы мы не были снабжены одной, конечно) - функция всегда возвращает "действительное" значение lvalue, поскольку оно принудительно вызывает вызов с lvalue.
Однако в С++ 11 теперь есть способ указать, что функция-член может быть вызвана только на lvalues, поэтому вы могли бы достичь той же цели, написав функцию-член:
Test &operator=(Test obj2) &
// ^^^
Теперь приведенный выше код с обвисшей ссылкой не сможет скомпилироваться.
NB. operator=
должен принимать правую сторону либо по значению, либо по константе. Принимая во внимание ценность, полезно при реализации идиома копирования и свопинга, техника для простой записи безопасных (но не обязательно самых быстрых) операторов копирования и переноса.
Ответ 9
Поскольку существует уже неявная функция перегрузки оператора для '=' в классе, чтобы сделать мелкое копирование. Поэтому даже если вы перегрузите функцию друга , вы никогда не сможете ее назвать, поскольку любой сделанный нами вызов вызовет неявный метод неполного копирования, а не перегруженную функцию друга.