Конструктор копирования вызывается с переменной, используемой только один раз. Может ли это быть в случае оптимизации компилятора, вместо этого вызывая конструктор перемещения? - PullRequest
0 голосов
/ 31 октября 2018

код:

#include <iostream>

class A {
public:
    A() {
    }

    A(const A& a) {
        std::cout << "Copy constructor" << std::endl;
    }

    A(A&& a) {
        std::cout << "Move constructor" << std::endl;
    }
};

int main()
{
    {//case 1
        A b = A(A());
    }
    std::cout << std::endl;
    {// case 2
        A a;
        A b = A(a);
    }
    std::cout << std::endl;
    {//case 3
        A a;
        A b = A(std::move(a));
    }
}

Вывод (с флагом компиляции -O3):

#case 1
#nothing printed

#case 2
Copy constructor

#case 3
Move constructor

В случае 2, почему конструктор копирования вызывается даже с максимальным уровнем оптимизации (-O3)? Я ожидал, что компилятор обнаружит, что переменная 'a' похожа на временную (потому что используется только для построения 'b') и скорее использует конструктор перемещения (как в случае 3).

Насколько мне известно, есть по крайней мере один случай (оптимизация возвращаемого значения), когда компилятор может изменить наблюдаемое поведение программы, избегая вызова конструктора копирования, который имеет побочный эффект.
Поэтому мне было интересно, возможно ли в случае 2, также в целях оптимизации, заменить вызов конструктора копирования конструктором перемещения, зная, что переменная a никогда не используется вне конструкции b.

Ответы [ 2 ]

0 голосов
/ 02 ноября 2018

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

Эти случаи явно определены стандартом, и, к сожалению, такие случаи, как ваш случай 2, не включены, поэтому компилятору не разрешается выполнять такую ​​оптимизацию, которая изменяет наблюдаемое поведение.


Связанная часть в стандарте [class.copy.elision] / 3:

В следующих контекстах инициализации копирования вместо операции копирования может использоваться операция перемещения:

  • Если выражение в операторе возврата ([stmt.return]) является (возможно, заключенным в скобки) id-выражением, которое именует объект с автоматической продолжительностью хранения, объявленной в теле или в объявлении параметра самого внутреннего элемента. включающая функция или лямбда-выражение, или

  • , если операндом выражения throw является имя энергонезависимого автоматического объекта (кроме параметра функции или catch-предложения), область действия которого не выходит за пределы самого внутреннего включающего блока try (если есть)

0 голосов
/ 31 октября 2018

Хотя можно получить временное lvalue без имени (см. Этот вопрос ), это не то, что здесь происходит.

В случае 2 a не является временным, а именованным lvalue, поэтому elison не происходит и вызывается конструктор копирования.

...