Кажется, что основной совет относительно rvalues 0x C++ состоит в том, чтобы добавить конструкторов перемещения и переместить операторы в Ваши классы до реализации по умолчанию компиляторов их.
Но ожидание является проигрывающей стратегией при использовании VC10 потому что автоматическая генерация, вероятно, не будет здесь до VC10 SP1, или в худшем случае, VC11. Вероятно, ожидание этого будет измеряться в годах.
Здесь находится моя проблема. Написание всего этого дублирующего кода не является забавой. И неприятно посмотреть на. Но это - хорошо принятая нагрузка для тех классов, которые считают медленными. Не так для сотен, если не тысячи, меньших классов.
:: вздохи:: C++ 0x, как предполагалось, позволял мне написать меньше кода, не больше!
И затем у меня была мысль. Совместно использованный многими, я предположил бы.
Почему не только передают все значением? Не будет станд.:: переместитесь +, копируют elision, делают это почти оптимальным?
OurClass::OurClass(const SomeClass& obj) : obj(obj) {}
SomeClass o;
OurClass(o); // single copy
OurClass(std::move(o)); // single copy
OurClass(SomeClass()); // single copy
Недостатки: потраченная впустую копия для rvalues.
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
Профессионалы: По-видимому, самое быстрое.
Недостатки: много кода!
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
не имеет никакого конструктора перемещения.
Что Вы думаете? Это корректно? Действительно ли понесенное перемещение является обычно приемлемой потерей при сравнении на пользу сокращению кода?
Меня заинтересовал ваш вопрос, потому что я был новичком в этой теме и провел некоторое исследование. Разрешите представить результаты.
Во-первых, ваш вздох.
:: sighs :: C ++ 0x должен был позволить мне писать меньше кода, а не больше!
это также должно дать вам лучший контроль над кодом. И это так. Я бы придерживался дополнительного конструктора:
OurClass::OurClass(SomeClass&& obj) : obj(std::move(obj)) {}
Я лично предпочитаю многословие в сложных и важных ситуациях, потому что это держит меня и возможных читателей моего кода в курсе.
возьмем, например, приведение в стиле C (T *) pT
и стандарт C ++ static_cast
Гораздо более подробный - но большой шаг вперед.
Во-вторых, я немного подозрительно отнесся к вашему примеру 3, последнему тесту. Я подумал, что может быть задействован другой конструктор перемещения для создания переданного по значению параметра из 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
, что означает, что мы проверяем "кошерный" способ, описанный в c ++ 0x!
Я ошибался, подозревая, что компилятор делает глупости; он сохранил дополнительный конструктор перемещения в третьем тестовом примере.
Но, честно говоря, мы должны учитывать еще два деструктора, вызываемых в предлагаемом обходном пути, но это наверняка незначительный недостаток, учитывая, что никакие действия не должны выполняться, если произошло перемещение разрушаемого объекта. . Тем не менее, это полезно знать.
В любом случае, я не собираюсь останавливаться на достигнутом, что лучше написать еще один конструктор в классе-оболочке. Это всего лишь вопрос нескольких строк, поскольку вся утомительная работа уже проделана в SomeClass
, который имеет , чтобы иметь конструктор перемещения.