Оптимизация на C ++: избегайте операции копирования - PullRequest
1 голос
/ 26 сентября 2019

У меня есть следующее:

#include <iostream>
#include <utility>

class T {
public:
    T() {std::cout << "Default constructor called" << std::endl;}
    T(const T&) {std::cout << "Copy constructor called" << std::endl;}
};

static T s_t;

T foo() {
    return s_t;
}

class A {
public:
    A() = delete;
    A(T t) : _t(t) {}
private:
    T _t;
};

int main() {

    std::cout << "Starting main" << std::endl;
    A a(foo());
    return 0;

}

Когда я компилирую его со строкой: g++ -std=c++17 -O3 test.cpp и запускаю, я получаю следующий вывод

Default constructor called
Starting main
Copy constructor called
Copy constructor called

Мой вопрос:поскольку конструктор A принимает объект r-значения типа T, который используется только для инициализации _t, компилятор может избежать второй операции копирования и просто скопировать s_t непосредственно в _t?

(я понимаю, что потенциально могу заменить вторую копию перемещением, реализовав конструктор перемещения для T)

1 Ответ

1 голос
/ 26 сентября 2019

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

http://eel.is/c++draft/class.copy.elision

Когда определенные критерии выполнены, реализация может быть опущенаконструкция копирования / перемещения объекта класса, даже если конструктор, выбранный для операции копирования / перемещения и / или деструктор для объекта, имеет побочные эффекты.

Но это допускается только в очень определенных случаях.обстоятельства:

Такое исключение операций копирования / перемещения, называемое разрешением копирования, допускается при следующих обстоятельствах (которые могут быть объединены для удаления нескольких копий):

  • воператор возврата в функции с типом возврата класса, когда выражением является имя энергонезависимого автоматического объекта [...]

вашей переменнойне имеет автоматической длительности хранения (т.е. это не локальная переменная).

  • в выражении броска, [...]

Мы не трow-выражение.

  • в сопрограмме [...]

Мы не в сопрограмме.

  • , когда объявление-исключение обработчика исключений ([кроме]) объявляет объект того же типа (за исключением квалификации cv), что и объект исключения ([исключением. Throw]) [...]

Это также не так.

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

Тем не менее, если компилятор может доказать отсутствие побочных эффектов (которые, в случае большинства компиляторов, включаютсистемные вызовы и распределение памяти), конечно же, можно удалить копию в соответствии с правилом «как будто», как и для всех других оптимизаций.

...