Конструктор копирования не вызывается при возврате из перегруженного оператора + в перегруженный оператор =

Рассмотрим следующий код:

#include <iostream>
using namespace std;
class loc 
{
    int longitude, latitude;
    public:
        loc() {}
        loc(int lg, int lt) 
        {
            longitude = lg;
            latitude = lt;
        }
        loc(const loc& l)
        {
            cout << "a" << endl;
        }
        loc operator = (loc op2)
        {
            longitude = op2.longitude;
            latitude = op2.latitude;
            return *this;
        }
        loc operator+(loc op2);
};
loc loc::operator+(loc op2) {
    loc temp;
    temp.longitude = op2.longitude + longitude;
    temp.latitude = op2.latitude + latitude;
    return temp;
}
int main()
{
    loc ob1(10, 20), ob2( 5, 30);
    ob1 = ob1 + ob2;
    return 0;
}

При компиляции этой программы с помощью команды: g++ file.cpp вывод был таким:

a
hello

А затем, скомпилировав эту программу с помощью команды: g++ -fno-elide-constructors file.cpp, вывод был таким:

a
a
a
hello

Мой вопрос:

В первом случае, почему опущены два конструктора копирования?

Какие конструкторы копирования в любом случае исключаются? Есть ли другой механизм для = оператора или + оператора

ИЗМЕНИТЬ

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


person tapananand    schedule 16.11.2014    source источник
comment
Конструкторы копирования опущены, потому что стандарт специально разрешает такое поведение.   -  person Alok Save    schedule 16.11.2014
comment
Этот класс не нуждается в собственном конструкторе копирования и операторе присваивания. Ваш оператор присваивания неверен, так как он должен возвращать неконстантную ссылку.   -  person Neil Kirk    schedule 16.11.2014
comment
@AlokSave: я спрашиваю, почему опущены два конструктора копирования. Обычно только один будет исключен.   -  person tapananand    schedule 16.11.2014
comment
Ваш оператор присваивания имеет неопределенное поведение. Вы ничего не возвращаете из него, и тип возвращаемого значения не является недействительным.   -  person Benjamin Lindley    schedule 16.11.2014
comment
@BenjaminLindley: внес изменения. Все еще получаю тот же результат.   -  person tapananand    schedule 16.11.2014
comment
en.wikipedia.org/wiki/Return_value_optimization   -  person Neil Kirk    schedule 16.11.2014
comment
@NeilKirk: сообщение уже помечено как RVO, мой вопрос заключается в том, почему происходят две копии-элизионы и где они происходят.   -  person tapananand    schedule 16.11.2014
comment
Это объясняется в статье.   -  person Neil Kirk    schedule 16.11.2014
comment
@remyabel: я не разрешаю несколько операций присваивания, например: a = b = c   -  person tapananand    schedule 16.11.2014
comment
Множественные операции присваивания - это то, что разрешено C++, и вы должны, как правило, поддерживать его и полагаться на то, что пользователь вашего класса будет разумным. Что, если кто-то написал универсальную шаблонную функцию, использующую эту функцию, и хочет использовать с ней ваш класс? Они не могут. Также интересно, что ваш оператор присваивания принимает параметр по значению, что в данном случае неэффективно.   -  person Neil Kirk    schedule 16.11.2014
comment
Теперь ваш оператор снова вернулся к неправильному типу возврата. Также вы дважды копируете новый объект. Передать по константной ссылке.   -  person Neil Kirk    schedule 16.11.2014
comment
@NeilKirk: отредактировано, но просто чтобы заметить, что это не производственный код, я пытаюсь правильно понять концепции.   -  person tapananand    schedule 16.11.2014
comment
@TapanAnand Ваш оператор присваивания должен возвращать ссылку, а не объект. Точнее: loc& operator = (const loc& op2) {//...// return *this;} Кроме того, ваш конструктор копирования не копирует. Это приводит к ошибкам, если вы пишете такой фальшивый копировальный центр.   -  person PaulMcKenzie    schedule 16.11.2014


Ответы (1)


Одна пропущенная копия — это возврат из op+. RVO позволяет создавать результат непосредственно в конечном пункте назначения, полностью опуская temp. Пропущенная копия от temp.

Вторая пропущенная копия передает результат op+, который является временным, в op=. Вместо этого он обеспечивает прямое построение результата op+ в параметре op2. Это не RVO, просто обычное выпадение временных.

person Alan Stokes    schedule 16.11.2014
comment
Если это так, то также должны быть вызваны два конструктора копирования — один для передачи аргумента по значению в +, а другой — для передачи по значению в =. Но здесь вызывается только один конструктор копирования. - person tapananand; 16.11.2014
comment
Второй вариант, который я перечислил, является передачей по значению в op=. (Почему бы вам просто не посмотреть на сгенерированный код?) - person Alan Stokes; 16.11.2014
comment
Согласно вашему ответу, первый исключенный конструктор - это временный объект, созданный для возврата, который непосредственно конструируется в место назначения (что на самом деле является аргументом = вы исключаете во втором абзаце). См. эту ссылку: stackoverflow.com/questions/26937270/ - person tapananand; 16.11.2014
comment
Да. И эта прямая конструкция сохраняет две копии: одну для возврата и одну для передачи аргумента последующему вызову. - person Alan Stokes; 17.11.2014
comment
Если конструкция возврата опущена, то она должна быть непосредственно встроена в пункт назначения, можете ли вы сказать, что такое пункт назначения в данном случае. Также как построены аргументы + и =? - person tapananand; 17.11.2014