Ответ 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
, которая должна иметь конструктор перемещения.