Оптимизация количества вызовов конструктора - PullRequest
13 голосов
/ 06 сентября 2011

На работе у нас есть класс с дорогим конструктором, поэтому мы бы хотели, чтобы он вызывался как можно меньше раз. Мы просмотрели его использование и попытались сделать код более дружественным к RVO, так сказать.

Однако мы нашли причуду в компиляторе g ++, где не поняли, что произошло.

Пожалуйста, рассмотрите две реализации оператора +

const Imaginary Imaginary::operator+(const Imaginary& rhs) const
{
    Imaginary tmp(*this);
    tmp.append(rhs);
    return tmp;
}

и

const Imaginary Imaginary::operator+(const Imaginary& rhs) const
{
    return Imaginary(*this).append(rhs);
}

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

int main(int argc, char* argv[])
{
    Imaginary x(1, 1);
    Imaginary y(2, 1);

    Imaginary c = x + y;
    return 0;
}

Я получаю эту распечатку с первой реализацией оператора +

int/int ctor
int/int ctor
Copy ctor

И я получаю следующее, когда используется второй вариант оператора +

int/int ctor
int/int ctor
Copy ctor
Copy ctor

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

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

Вероятно, мне следует добавить это, когда мы добавляем --no-elide-constructors в качестве флага для g ++ Я получаю следующую распечатку

int/int ctor
int/int ctor
Copy ctor
Copy ctor
Copy ctor

С уважением, Маттиас

Ответы [ 2 ]

5 голосов
/ 06 сентября 2011

Если компилятор не может встроить append, он не может определить, что возвращаемое значение является целевым объектом.Тогда он не знает, что временный объект возвращается, и не может создать его на месте.

Вы бы имели такое же поведение с:

Imaginary tmp(*this);
return tmp.append(rhs);

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

0 голосов
/ 19 декабря 2013

Стефан Т. Лававей упоминает в http://channel9.msdn.com/Events/GoingNative/2013/Don-t-Help-the-Compiler, что (N) RVO происходит только тогда, когда тип возвращаемого значения точно совпадает с типом, возвращаемым из метода.

Например:

string foo() {string tmp; return tmp;}  // Same type, uses NRVO or automatic move.
string foo() {const string& tmp = "bar"; return tmp;}  // Types differ, no NRVO, nor automatic move.
string foo() {string tmp; string& ref = tmp; return ref;}  // Types differ, no NRVO, nor automatic move.
string foo() {string tmp; return (string&) tmp;}  // Types differ, no NRVO, nor automatic move.

(ср. http://coliru.stacked -crooked.com / a / 79e79e5bb0350584 )

Я думаю, append возвращает ссылку к мнимому, и поскольку Imaginary& не относится к тому же типу, что и Imaginary, это предотвращает (N) RVO.

...