Подтвердить что ты не робот

Копировать elision для аргументов pass-by-value

Учитывая

struct Range{
    Range(double from, double to) : from(from), to(to) {}
    double from;
    double to;
};

struct Box{
    Box(Range x, Range y) : x(x), y(y) {}
    Range x;
    Range y;
};

предположим, что мы запускаем Box box(Range(0.0,1.0),Range(0.0,2.0)).

Можно ли использовать современный компилятор с оптимизацией во избежание копирования объектов Range вообще во время этой конструкции? (т.е. построить объекты Range внутри box для начала?)

4b9b3361

Ответ 1

Фактически две копии выполняются для каждого объекта Range, переданного конструктору. Первое происходит при копировании временного объекта Range в параметр функции. Это может быть отменено в соответствии с ссылкой, приведенной в ответе 101010. Существуют особые обстоятельства, в которых может выполняться копирование.

Вторая копия происходит при копировании параметра функции в член (как указано в списке инициализации конструктора). Это не может быть устранено, и поэтому вы все равно видите, что для каждого параметра в ответе YSC делается одна копия.

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

Однако компилятор всегда может вносить изменения, если они не изменяют наблюдаемое поведение программы (это известно как правило "как-если" ). Это означает, что если конструктор копирования не имеет побочных эффектов и удаление вызова конструктора не изменит результат, компилятор может удалить даже вторую копию.

Это можно увидеть, проанализировав сгенерированную сборку. В этом примере компилятор оптимизирует не только копии, но даже конструкцию самого объекта Box:

Box box(Range(a,b),Range(c,d));
std::cout << box.x.from;

Создает идентичную сборку как:

std::cout << a;

Ответ 2

Он должен, но Мне не удалось заставить его работать (живой пример). Компилятор может обнаружить побочный эффект конструкторов и решить не идти с копией.

#include <iostream>

struct Range{
    Range(double from, double to) : from(from), to(to) { std::cout << "Range(double,double)" << std::endl; }
    Range(const Range& other) : from(other.from), to(other.to) { std::cout << "Range(const Range&)" << std::endl; }
    double from;
    double to;
};

struct Box{
    Box(Range x, Range y) : x(x), y(y) { std::cout << "Box(Range,Range)" << std::endl; }
    Box(const Box& other) : x(other.x), y(other.y) { std::cout << "Box(const Box&)" << std::endl; }
    Range x;
    Range y;
};


int main(int argc, char** argv)
{
    (void) argv;
    const Box box(Range(argc, 1.0), Range(0.0, 2.0));
    std::cout << box.x.from << std::endl;
    return 0;
}

Скомпилировать и запустить:

clang++ -std=c++14 -O3 -Wall -Wextra -pedantic -Werror -pthread main.cpp && ./a.out

Вывод:

Range(double,double)
Range(double,double)
Range(const Range&)
Range(const Range&)
Box(Range,Range)
1

Ответ 3

Да, это может быть, В частности, такой тип контекста копирования подпадает под критерий разрешения копии, указанный в 12.8/p31.3 Копирование и перемещение объектов класса [class.copy] стандарта:

(31.3) - когда объект временного класса, который не привязан к ссылке (12.2) будет скопирован/перенесен в объект класса с тем же типом (игнорируя cv-квалификацию), операция копирования/перемещения может быть опущена построение временного объекта непосредственно в цель пропущено копирование/перемещение.

Любой спуск-компилятор применяет копирование в определенном контексте. Однако в примере OP присутствуют две копии.

  • Временные объекты, переданные в конструкторе (это может быть указано в стандарте, как указано выше).
  • Копии в списке инициализаторов конструктора Box (это не может быть отменено).

Вы можете увидеть это в этой демо, где конструктор копирования вызывается только 2 раза.

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

Ответ 4

Тот факт, что он может, не означает, что это, безусловно, будет. Смотрите в этом Demo, очевидно, что вы создаете две копии. Подсказка: вывод содержит два раза:

сделана копия

сделана копия