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

Перегрузка по ссылке, по сравнению с единственной передачей по значению + std:: move?

Кажется, что основной совет относительно С++ 0x rvalues ​​- добавить конструкторы перемещения и переместить операторы в ваши классы, пока компиляторы не будут использовать их по умолчанию.

Но ожидание - это проигрышная стратегия, если вы используете VC10, потому что автоматическая генерация, вероятно, не будет здесь до VC10 SP1, или в худшем случае, VC11. Вероятно, ожидание этого будет измеряться годами.

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

:: sighs:: С++ 0x должен был позволить мне писать меньше кода, а не больше!

И тогда я подумал. Я бы предположил, что меня разделяют многие.

Почему бы просто не передать все по стоимости? Не будет ли std:: move + copy elision сделать это почти оптимальным?

Пример 1 - Типичный конструктор Pre-0x

OurClass::OurClass(const SomeClass& obj) : obj(obj) {}

SomeClass o;
OurClass(o);            // single copy
OurClass(std::move(o)); // single copy
OurClass(SomeClass());  // single copy

Минусы: Пропущенная копия для rvalues.

Пример 2 - Рекомендуемый С++ 0x?

OurClass::OurClass(const SomeClass& obj) : obj(obj) {}
OurClass::OurClass(SomeClass&& obj) : obj(std::move(obj)) {}

SomeClass o;
OurClass(o);            // single copy
OurClass(std::move(o)); // zero copies, one move
OurClass(SomeClass());  // zero copies, one move

Плюсы: Предположительно самый быстрый.
Минусы: Много кода!

Пример 3 - Пошаговое значение + std:: move

OurClass::OurClass(SomeClass obj) : obj(std::move(obj)) {}

SomeClass o;
OurClass(o);            // single copy, one move
OurClass(std::move(o)); // zero copies, two moves
OurClass(SomeClass());  // zero copies, one move

Плюсы: Нет дополнительного кода.
Минусы: Впустую движение в случаях 1 и 2. Производительность сильно пострадает, если SomeClass не имеет конструктора перемещения.


Как вы думаете? Это верно? Понесённый понесёт общеприемлемую потерю по сравнению с преимуществом сокращения кода?

4b9b3361

Ответ 1

Меня интересовал ваш вопрос, потому что я был новичком в этой теме и сделал некоторые исследования. Позвольте мне представить результаты.

Во-первых, ваш вздох.

:: sighs:: С++ 0x должен был позволить мне писать меньше кода, а не больше!

то, что также предполагается, - дать вам лучший контроль над кодом. И это так. Я хотел бы добавить дополнительный конструктор:

OurClass::OurClass(SomeClass&& obj) : obj(std::move(obj)) {}

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

возьмите, например, C-style cast (T*)pT и С++ standard static_cast<T*>(pT) Гораздо более подробный - но большой шаг вперед.

Во-вторых, я немного подозрительно относился к вашему примеру 3, последнему тестовому примеру. Я думал, что для создания параметра by-by-value из rvalue может быть другой конструктор перемещения. Итак, я создал какой-то быстрый проект в своем новом VS2010 и получил некоторые пояснения. Я отправлю код здесь, а также результаты.

источник:

// test.cpp : Defines the entry point for the console application.
//

#include "stdafx.h"

#include <utility>
#include <iostream>

class SomeClass{
    mutable int *pVal;
public:
    int Val() const { return *pVal; };
    SomeClass(int val){
        pVal = new int(val);
        std::cout << "SomeClass constructor(pVal = 0x" << std::hex << pVal << std::dec << ")" << std::endl;
    }
    SomeClass(const SomeClass& r){
        pVal = new int(r.Val());
        std::cout << "SomeClass copy constructor(pVal = 0x" << std::hex << pVal << std::dec << ")" << std::endl;
    }   
    SomeClass(const SomeClass&& r){
        pVal = r.pVal;
        r.pVal = 0;
        std::cout << "SomeClass move constructor(pVal = 0x" << std::hex << pVal << std::dec << ")" << std::endl;
    }
    ~SomeClass(){
        if(pVal)
            delete pVal;
        std::cout << "SomeClass destructor(pVal = 0x" << std::hex << pVal << std::dec << ")" << std::endl;
    }
};

class OtherClass{
    SomeClass sc;
public:
    OtherClass(int val):sc(val){
    }

Обратите внимание на этот раздел:

#if 1
    OtherClass(SomeClass r):sc(std::move(r)){
    }
#else
    OtherClass(const SomeClass& r):sc(r){
    }   
    OtherClass(const SomeClass&& r):sc(std::move(r)){
    }
#endif

...

    int Val(){ return sc.Val(); }
    ~OtherClass(){
    }
};

#define ECHO(expr)  std::cout << std::endl << "line " << __LINE__ << ":\t" #expr ":" << std::endl; expr

int _tmain(int argc, _TCHAR* argv[])
{
    volatile int __dummy = 0;
    ECHO(SomeClass o(10));

    ECHO(OtherClass oo1(o));            
    __dummy += oo1.Val();
    ECHO(OtherClass oo2(std::move(o))); 
    __dummy += oo2.Val();
    ECHO(OtherClass oo3(SomeClass(20)));  
    __dummy += oo3.Val();

    ECHO(std::cout << __dummy << std::endl);
    ECHO(return 0);
}

Как вы уже отметили, есть переключатель времени компиляции, который позволяет мне протестировать два подхода.

Результаты лучше всего просматриваются в режиме сравнения текста, слева вы можете увидеть компиляцию #if 1, что означает, что мы проверяем предлагаемый обходной путь, а справа - #if 0, что означает, что мы проверяем "кошерный" способ, описанный в С++ 0x!

Я ошибался, подозревая, что компилятор делает глупые вещи; он сохранил дополнительный конструктор перемещения в третьем тестовом случае.

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

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