Как эффективно привязать lvalue или rvalue к одной и той же ссылке?

Скажем, у вас есть функция C++, которая использует измененную версию параметра (const).

MyObject alter_obj( MyObject const & obj ); // Creates new, altered object

void func( MyObject const & original ) {
    MyObject const & altered( alter_obj( original ) );
    // ...
}

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

Также было бы эффективно, если бы вы вообще не вносили изменения:

void func( MyObject const & original ) {
    MyObject const & not_altered( original );
    // ...
}

Дополнительные ссылки на данный объект в основном бесплатны, без каких-либо потерь производительности при создании копии.

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

MyObject alter_obj( MyObject const & obj ); // Creates new, altered object

void func( MyObject const & original ) {
    // ...
    MyObject const & possibly_altered( 
        apply_alteration ? 
        alter_obj( original ) : 
        original 
    );
    // ...
}

Однако оказалось, что этот подход не так эффективен, как хотелось бы. Тернарный оператор, по-видимому, требует, чтобы последние два параметра соответствовали статусу lvalue/rvalue, а не только номинальному типу. Это означает, что при выборе ветви false (без изменений) временное значение rvalue создается путем вызова конструктора копирования MyObject на original. Если MyObject не является тривиальным для копирования, существует потенциальная потеря производительности из-за создания этой «ложной» копии.

Есть ли хороший способ обойти это? Можно ли эффективно привязать локальную ссылку либо к другой существующей ссылке, либо к временной (выбор на основе значений времени выполнения) без создания дополнительных копий?


person R.M.    schedule 08.02.2019    source источник
comment
Человеку, который проголосовал за закрытие из-за неясности того, о чем вы спрашиваете, я был бы рад уточнить, дали ли вы общее представление о том, что вам неясно. -- Суть проблемы в том, что код в третьем блоке создает копию, даже если apply_alteration имеет значение false. Я хочу сделать то, что делает третий блок, но без этой копии.   -  person R.M.    schedule 09.02.2019


Ответы (2)


Я бы создал отдельную функцию, которая принимает ссылку и вызывает ее, например:

void func( MyObject const & original ) {
    if (apply_alteration)
        func_internal(alter_obj(original));
    else
        func_internal(original);
}

void func_internal( MyObject const & possibly_altered) {
    // ...
}
person Iłya Bursov    schedule 08.02.2019
comment
Я согласен с этим: операция над объектом (здесь func_internal) должна быть его собственной функцией, и логика решения, над каким объектом работать, отличается от этой. - person M.M; 09.02.2019

Может быть, добавить второй параметр к func()?

void func( MyObject const& original, bool modify = false ) {
    if ( modify )  MyObject const & altered( alter_obj( original ) );
    else MyObject const & not_altered( original ); 
}

Может достичь того, что вы ищете ...

person Francis Cugler    schedule 09.02.2019
comment
Придумать modify bool здесь не проблема. Трудность заключается в том, чтобы поместить соответствующий объект в ту же переменную. В любом случае, ваш подход if/else на самом деле не работает, поскольку переменные, объявленные в предложении if и/или else, не видны никакому внешнему коду. (Это означает, что вам придется продублировать следующий код, где вы фактически используете переменную в блоках if и else.) - person R.M.; 09.02.2019
comment
@ Р.М. О, хорошо, я понимаю, что вы говорите... Но я думал, что параметр bool не объявлялся в операторе if, поскольку он был передан в параметре, и в зависимости от этого параметра он вызывал бы соответствующую функцию... - person Francis Cugler; 09.02.2019