Использование ссылок Rvalue и Simple Factory

Я недавно узнал о std::move а также rvalue ссылки, и хочу знать, является ли мое использование их уместным и эффективным.

Считайте это простым Image класс, который просто хранит массив unsigned char значения, представляющие пиксели. (Обратите внимание, что на самом деле членов больше, чем одного массива символов, но здесь я упрощаю для удобства чтения.)

using PixelContainer = std::vector<unsigned char>;

class Image {
public:
Image(PixelContainer&& pixels) : m_pixels(std::move(pixels)) {};

Image& operator=(Image&& image) { m_pixels = std::move(image.m_pixels); return *this; };

private:
PixelContainer m_pixels;
};

А теперь рассмотрим этот статический метод ImageFactory класс, который принимает имя файла .png и возвращает Image объект заполнен соответствующими значениями пикселей.

Image ImageFactory::loadImage(const char* filename) {
PixelContainer temp_pixels;

// ... fill pixels from file (details not relevant here)

Image temp_image(std::move(temp_pixels));

return temp_image;
}

Наконец, вместе они используются в коде как таковом:

Image image = ImageFactory::loadImage("image.png");

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

Мой вопрос: имеет ли смысл моя реализация, и если она не имеет существенных недостатков, можно ли ее еще улучшить?

2

Решение

Давайте разберемся! Я добавил такую ​​функцию, чтобы она содержала ваш последний бит кода:

Image outer() {
Image image = ImageFactory::loadImage("image.png");
return image;
}

Мне также пришлось добавить обычный конструктор копирования для Image (Я использовал = default определить его), потому что, даже если он на самом деле не используется, он должен возвращаться по значению.

Затем я скомпилировал с помощью Clang -O2 -g -S и посмотрел на сборку. В основном это код для инициализации вектора: call operator new и настройте внутренние указатели вектора. Там нет копирования, код выглядит достаточно чистым и эффективным. И, как вы можете надеяться, код для outer() почти идентичен коду для loadImage() (последний встраивается в первый, потому что я помещаю все в одну единицу перевода).

Для справки вот сборка, которую я получил:

outer():                             ## @_Z5outerv
pushq   %rbp
movq    %rsp, %rbp
pushq   %rbx
pushq   %rax
movq    %rdi, %rbx
movl    $100000000, %edi        ## imm = 0x5F5E100
callq   operator new(unsigned long)
movd    %rax, %xmm0
movlhps %xmm0, %xmm0            ## xmm0 = xmm0[0,0]
movq    $-100000000, %rcx       ## imm = 0xFFFFFFFFFA0A1F00
movq    %rax, %rsi
LBB1_1:                                 ## =>This Inner Loop Header: Depth=1
testq   %rsi, %rsi
movl    $0, %edx
je      LBB1_3
movb    $0, (%rsi)
movaps  %xmm0, %xmm1
punpckhqdq      %xmm1, %xmm1    ## xmm1 = xmm1[1,1]
movd    %xmm1, %rdx
LBB1_3:                                 ## %_ZNSt3__116allocator_traits...
incq    %rdx
movd    %rdx, %xmm1
punpcklqdq      %xmm1, %xmm0    ## xmm0 = xmm0[0],xmm1[0]
incq    %rcx
movq    %rdx, %rsi
jne     LBB1_1
## BB#4:                                ## %_ZN12ImageFactory9loadImageEPKc.exit
leaq    100000000(%rax), %rax
movdqu  %xmm0, (%rbx)
movq    %rax, 16(%rbx)
movq    %rbx, %rax
addq    $8, %rsp
popq    %rbx
popq    %rbp
retq
4

Другие решения


По вопросам рекламы [email protected]