Информация об изменениях

Сообщение Re: Правильно ли я понимаю перемещение от 19.05.2016 19:59

Изменено 19.05.2016 20:05 Erop

Здравствуйте, dosik, Вы писали:


D>Таким образом мы избавляемся от одного конструктора и одного деструктора? Или же конструкторы и деструкторы будут вызваны оба раза, а при перемещении произойдет лишь "передача" внутренностей?


По стандарту 98 поведение должно быть таким:

std::vector<some_type>& some_func(void* resultBuffer, ...) {
    std::vector<some_type> result;
    ......
    //Some code to fill vector
    ......
    return *new(resultBffer) std::vector<some_type>( result );
}
....

// выделили место под std::vector<some_type> need;
std::vector<some_type>&need = some_func(место_под_need, ...);
// с этого момента вызывающая сторона отвечает за разрушение need


и с 98 было в стандарте же можно делать ВНУТРИ ФУНКЦИИ RVO, а с 03 и NRVO
В данном случае NRVO будет означать следующее:
std::vector<some_type>& some_func(void* resultBuffer, ...) {
    std::vector<some_type>& result = *new(resultBffer) std::vector<some_type>( ;
    ......
    //Some code to fill vector
    ......
    return result;
}


RVO же, означает следующее:
std::vector<some_type>& some_func(void* resultBuffer, ...) {
    
    //тут код, который вычисляет параметры конструктора std::vector<some_type>
    ......
    return *new(resultBffer) std::vector<some_type>( те самые параметры конструктора );
}


Для этого исходную some_func надо писать так:
std::vector<some_type> some_func(...) {
    
    //тут код, который вычисляет параметры конструктора std::vector<some_type>
    ......
    return std::vector<some_type>( те самые параметры конструктора );
}

И для такой редакции в С++11 будет доступна move-семантика. То есть эквивалентный код будет такой:
std::vector<some_type>& some_func(void* resultBuffer, ...) {
    
    //тут код, который вычисляет параметры конструктора std::vector<some_type>
    ......
    std::vector<some_type> temporary_object( те самые параметры конструктора );
    return *new(resultBffer) std::vector<some_type>( static_cast<std::vector<some_type>&&>( temporary_object ) );
}

Правда, насколько я понимаю, RVO всё равно доступна компилятору, так как у std::vector есть доступный конструктор копии.
Чтобы move-семантика была доступна в оригинальной версии, надо писать так:
std::vector<some_type> some_func(void* resultBuffer, ...) {
    std::vector<some_type> result;
    ......
    //Some code to fill vector
    ......
    return std::move( result );
}


Но я не понимаю зачем, если у возвращаемого типа есть доступный конструктор копии.
Другое дело, если мы, например, std::unique_ptr захотим вернуть...

При этом в любом случае ВЫЗЫВАБЩИЙ код от того, что ВНУТРИ функции пишут зависеть не должен, если не включена оптимизация вызывающего кода, использующая доступ к определению тела функции в точке вызова (то, что обычно понимают под inline)
Здравствуйте, dosik, Вы писали:


D>Таким образом мы избавляемся от одного конструктора и одного деструктора? Или же конструкторы и деструкторы будут вызваны оба раза, а при перемещении произойдет лишь "передача" внутренностей?


По стандарту 98 поведение должно быть таким:

std::vector<some_type>& some_func(void* resultBuffer, ...) {
    std::vector<some_type> result;
    ......
    //Some code to fill vector
    ......
    return *::new(resultBffer) std::vector<some_type>( result );
}
....

// выделили место под std::vector<some_type> need;
std::vector<some_type>&need = some_func(место_под_need, ...);
// с этого момента вызывающая сторона отвечает за разрушение need


и с 98 было в стандарте же можно делать ВНУТРИ ФУНКЦИИ RVO, а с 03 и NRVO
В данном случае NRVO будет означать следующее:
std::vector<some_type>& some_func(void* resultBuffer, ...) {
    std::vector<some_type>& result = *new(resultBffer) std::vector<some_type>;
    // с этого момента и до return some_func отвечает за разрушение result 
    ......
    //Some code to fill vector
    ......
    return result;  // с этого момента за разрушение result отвечает вызывающая сторона
}


RVO же, означает следующее:
std::vector<some_type>& some_func(void* resultBuffer, ...) {
    
    //тут код, который вычисляет параметры конструктора std::vector<some_type>
    ......
    return *::new(resultBffer) std::vector<some_type>( те самые параметры конструктора );
}


Для этого исходную some_func надо писать так:
std::vector<some_type> some_func(...) {
    
    //тут код, который вычисляет параметры конструктора std::vector<some_type>
    ......
    return std::vector<some_type>( те самые параметры конструктора );
}

И для такой редакции в С++11 будет доступна move-семантика. То есть эквивалентный код будет такой:
std::vector<some_type>& some_func(void* resultBuffer, ...) {
    
    //тут код, который вычисляет параметры конструктора std::vector<some_type>
    ......
    std::vector<some_type> __temporary_object__( те самые параметры конструктора );
    return *new(resultBffer) std::vector<some_type>( static_cast<std::vector<some_type>&&>( __temporary_object__ ) );
}

Правда, насколько я понимаю, RVO всё равно доступна компилятору, так как у std::vector есть доступный конструктор копии.
Чтобы move-семантика была доступна в оригинальной версии, надо писать так:
std::vector<some_type> some_func(void* resultBuffer, ...) {
    std::vector<some_type> result;
    ......
    //Some code to fill vector
    ......
    return std::move( result );
}


Но я не понимаю зачем, если у возвращаемого типа есть доступный конструктор копии.
Другое дело, если мы, например, std::unique_ptr захотим вернуть...

При этом в любом случае ВЫЗЫВАБЩИЙ код от того, что ВНУТРИ функции пишут зависеть не должен, если не включена оптимизация вызывающего кода, использующая доступ к определению тела функции в точке вызова (то, что обычно понимают под inline)