resueltos programacion polimorfismo orientada objetos metodos herencia ejercicios ejemplos constructores codigo clases c++ c++11 rvalue-reference move-constructor

programacion - Pasando/Moviendo parámetros de un constructor en C++ 0x



polimorfismo c++ (3)

Si tengo un constructor con n parámetros tales que cualquier argumento puede ser un rvalue y lvalue. ¿Es posible hacer esto con la semántica de movimiento para los valores de r sin escribir 2 ^ n constructores para cada combinación posible de valor de r / valor


Dependiendo de qué compilador de c ++ esté utilizando, puede buscar en "funciones con listas de argumentos variables"

La idea es que puede pasar tantos parámetros como desee al método y simplemente se rellena en una matriz que puede recorrer.

Para Microsoft C ++, las siguientes entradas de blog pueden ser útiles:

http://msdn.microsoft.com/en-us/library/fxhdxye9(v=VS.100).aspx http://blogs.msdn.com/b/slippman/archive/2004/02/16/73932. aspx


Toma el siguiente enlace de código ideone .

#include <iostream> class A { public: A() : i(0) {} A(const A& a) : i(a.i) { std::cout << "Copy A" << std::endl; } A(A&& a) : i(a.i) { std::cout << "Move A" << std::endl; } int i; }; template <class T> class B1 { public: template <class T1, class T2> B1(T1&& x1_, T2&& x2_) : x1(std::forward<T1>(x1_)), x2(std::forward<T2>(x2_)) {} B1(const B1<T>& x) : x1(x.x1), x2(x.x2) { std::cout << "Copy B1" << std::endl; } B1(B1<T>&& x) : x1(std::move(x.x1)), x2(std::move(x.x2)) { std::cout << "Move B1" << std::endl; } private: T x1; T x2; }; template <class T> class B2 { public: B2(T x1_, T x2_) : x1(std::move(x1_)), x2(std::move(x2_)) {} B2(const B2<T>& x) : x1(x.x1), x2(x.x2) { std::cout << "Copy B2" << std::endl; } B2(B2<T>&& x) : x1(std::move(x.x1)), x2(std::move(x.x2)) { std::cout << "Move B2" << std::endl; } private: T x1; T x2; }; A&& inc_a(A&& a) { ++a.i; return static_cast<A&&>(a); } A inc_a(const A& a) { A a1 = a; ++a1.i; return a1; } int main() { A a1; A a2; std::cout << "1" << std::endl; B1<A> b1(a1,a2); std::cout << "2" << std::endl; B1<A> b2(a1,A()); std::cout << "3" << std::endl; B1<A> b3(A(),a2); std::cout << "4" << std::endl; B1<A> b4(A(),A()); std::cout << "5" << std::endl; B2<A> b5(a1,a2); std::cout << "6" << std::endl; B2<A> b6(a1,A()); std::cout << "7" << std::endl; B2<A> b7(A(),a2); std::cout << "8" << std::endl; B2<A> b8(A(),A()); std::cout << "9" << std::endl; std::cout << std::endl; std::cout << "11" << std::endl; B1<A> b11(a1,a2); std::cout << "12" << std::endl; B1<A> b12(a1,inc_a(A())); std::cout << "13" << std::endl; B1<A> b13(inc_a(A()),a2); std::cout << "14" << std::endl; B1<A> b14(inc_a(A()),inc_a(A())); std::cout << "15" << std::endl; B2<A> b15(a1,a2); std::cout << "16" << std::endl; B2<A> b16(a1,inc_a(A())); std::cout << "17" << std::endl; B2<A> b17(inc_a(A()),a2); std::cout << "18" << std::endl; B2<A> b18(inc_a(A()),inc_a(A())); std::cout << "19" << std::endl; }

Lo que da como resultado lo siguiente:

1 Copy A Copy A 2 Copy A Move A 3 Move A Copy A 4 5 Copy A Copy A Move A Move A 6 Copy A Move A Move A 7 Copy A Move A Move A 8 9 11 Copy A Copy A 12 Copy A Move A 13 Move A Copy A 14 Move A Move A 15 Copy A Copy A Move A Move A 16 Move A Copy A Move A Move A 17 Copy A Move A Move A Move A 18 Move A Move A Move A Move A 19

Como puede verse, el enfoque de paso por valor en B2 provoca movimientos adicionales para cada argumento en todos los casos, excepto cuando el argumento es un valor predefinido.

Si desea obtener el mejor rendimiento, sugiero el enfoque de plantilla en B1 . De esta manera, efectivamente tiene un código separado para los casos de copia y movimiento, y por lo tanto, solo se necesita una copia o un solo movimiento. En el enfoque de paso por valor, se requieren al menos dos movimientos / copias, excepto en el caso de prvalue donde el compilador puede construir el valor en el lugar del argumento, en el que solo se requiere un movimiento.


Tomas cada uno por valor, así:

struct foo { foo(std::string s, bar b, qux q) : mS(std::move(s)), mB(std::move(b)), mQ(std::move(q)) {} std::string mS; bar mB; qux mQ; };

La inicialización de los parámetros de la función por el argumento será un constructor de copia o un constructor de movimiento. A partir de ahí, simplemente mueve los valores de los parámetros de la función a sus variables miembro.

Recuerde: las semánticas de copia y movimiento son un servicio proporcionado por la clase, no por usted . En C ++ 0x, ya no necesita preocuparse por cómo obtener su propia "copia" de los datos; solo pídelo y deja que la clase lo haga:

foo f("temporary string is never copied", bar(), quz()); // no copies, only moves foo ff(f.mS, f.mB, f.mQ); // copies needed, will copy foo fff("another temp", f.mB, f.mQ); // move string, copy others

Nota: su constructor solo toma valores, esos valores descubrirán cómo construirse ellos mismos. Desde allí, por supuesto, depende de usted moverlos a donde los quiera.

Esto se aplica en todas partes. ¿Tiene una función que necesita una copia? Hazlo en la lista de parámetros:

void mutates_copy(std::string s) { s[0] = ''A''; // modify copy } mutates_copy("no copies, only moves!"); std::string myValue = "don''t modify me"; mutates_copy(myValue); // makes copy as needed mutates_copy(std::move(myValue)); // move it, i''m done with it

En C ++ 03, podrías emularlo bastante bien, pero no era común (en mi experiencia):

struct foo { foo(std::string s, bar b, qux q) // have to pay for default construction { using std::swap; // swaps should be cheap in any sane program swap(s, mS); // this is effectively what swap(b, mB); // move-constructors do now, swap(q, mQ); // so a reasonable emulation } std::string mS; bar mB; qux mQ; };