2

Думаю, стоит сразу начать с кода:

#include <iostream>

class C
{
    public:

        C(int a);

        ~C();

        C(const C &rhs);

        C& operator=(const C &rhs);

        C(C &&rhs);

        C& operator=(C &&rhs);

        int _a;
};

void func(C a)
{
    std::cout << a._a << std::endl;
}

int main()
{
    func(C{1});

    std::cout << "Exit" << std::endl;
    return 0;
}

В классе C реализованы все автоматически генерируемые методы (конструктор, деструктор, конструкторы и опрераторы присваивания для копирования и перемещения).

Вывод дебажной информации из методов показал, что для объектов, передаваемых в функцию func (первая строка функции main), вызываются только простые конструкторы.

Возможно, вопрос покажется глупым, но для меня это стало открытием. Почему же не вызывается никакая копирующая операция? Параметры, передаваемые в функцию func, просто "становятся" локальными параметрами самой функции. Действительно ли так было всегда или же это оптимизация современных компиляторов?

Если что, я использую GNU g++ 6.3.1 с поддержкой 11 и 14 стандартов.

AccumPlus
  • 1,506
  • Было бы хорошо, если бы Вы сократили пример до [mcve], а так же добавили имеющийся вывод и тот, который Вам кажется более очевидным. – αλεχολυτ May 05 '17 at 07:53
  • Ну, тела функций с выводом можно было бы и оставить, а вот кол-во аргументов сократить. Например, наличие b не несёт никакой смысловой нагрузки. – αλεχολυτ May 05 '17 at 08:16
  • А функция func что-то делает с a и b? – Unick May 05 '17 at 08:21
  • @Unick как вариант, просто выводит значение параметров в стандартный поток вывода. – AccumPlus May 05 '17 at 08:31
  • @Abyx ошибся я, не паникуйте :) – AccumPlus May 05 '17 at 10:02

2 Answers2

3

Исключение лишних копирующих/перемещающих операций обусловлено наличием так называемого Copy/Move Elision в языке. В доступном онлайн черновике стандарта это п. 15.8.3. Он гласит, что при определённых условиях разрешается выкидывать из кода вызов копирующих/перемещающих конструкторов.

Для компиляторов gcc/clang отключение этой оптимизации может быть достигнуто за счёт ключа -fno-elide-constructors.

Причем стоит заметить, что начиная с C++17 упомянутая оптимизация является обязательной и даже наличие ключа не приводит к появлению копирования/перемещения:

  • С++14

    Default constructor   
    Move constructor  
    1  
    Destructor  
    Destructor  
    
  • С++17

    Default constructor
    1
    Destructor
    
αλεχολυτ
  • 28,987
  • 13
  • 60
  • 119
2

Компилятор просто оптимизирует такие простые классы, вплоть до интерпретации в виде простых типов. Вот пример того, как компилятор выполняет оптимизации: ссылка

Кстати, у вас неправильно объявлены конструктор и оператор перемещения:

C(const C &&rhs);
C& operator=(const C &&rhs);

Rvalue не может быть константным, добавление const автоматически преобразует rvalue в const lvalue, т.е. вы просто два раза объявляете конструктор и оператор копирования.

Ariox
  • 2,990
  • Но тем не менее эти операции вызываются в нужное время. Сейчас почитаю ссылку, спасибо! – AccumPlus May 05 '17 at 09:12