sobre - ¿Es una buena forma de evitar que el código C++ 03 se realice de forma subóptima en C++ 11?
memetica pdf (2)
Tenía algún código C ++ 03 que implementaba swap
para ciertas clases, para hacer que std::sort
(y otras funciones) fuera rápido.
Desafortunadamente para mí, std::sort
ahora parece usar std::move
, lo que significa que mi código ahora es mucho más lento que en C ++ 03.
Sé que puedo usar #if __cplusplus >= 201103L
para definir condicionalmente un operador de movimiento-constructor / movimiento-asignación, pero me pregunto si hay una mejor manera de no usar hacks de preprocesador.
(Me gustaría evitar los hacks proprocesadores porque serían feos, ya que no solo tengo que probar versiones de compilador como _MSC_VER >= 1600
, sino también porque no funcionan bien con herramientas como LZZ que no funcionan). reconozca la sintaxis de movimiento de C ++ 11 pero me obliga a preprocesar el código.)
Echa un vistazo a Boost.Move . Tiene emulación de movimiento para c ++ 03. Tal vez pueda ayudar, pero no eché un vistazo a los detalles.
Parece que la pregunta realmente es: ¿cómo pueden implementarse un constructor de movimientos y una asignación de movimientos con compiladores C ++ 03?
La respuesta simple es: ¡no pueden! Sin embargo, la respuesta simple descuida la posibilidad de crear algo que sea un código C ++ 03 perfectamente válido y que se convierta en constructor de movimiento y asignación de movimiento con un compilador de C ++ 11. Este enfoque deberá usar una piratería de preprocesador, pero ese bit solo se usa para crear un encabezado que define algunas herramientas utilizadas para la implementación real.
Aquí hay un archivo de encabezado simple que compila alegremente sin ninguna advertencia con clang y gcc con C ++ 11 habilitado o deshabilitado:
// file: movetools.hpp
#ifndef INCLUDED_MOVETOOLS
#define INCLUDED_MOVETOOLS
INCLUDED_MOVETOOLS
namespace mt
{
#if __cplusplus < 201103L
template <typename T>
class rvalue_reference {
T* ptr;
public:
rvalue_reference(T& other): ptr(&other) {}
operator T&() const { return *this->ptr; }
};
#else
template <typename T>
using rvalue_reference = T&&;
#endif
template <typename T>
rvalue_reference<T> move(T& obj) {
return static_cast<rvalue_reference<T> >(obj);
}
}
#endif
La característica básica es definir una plantilla mt::rvalue_reference<T>
que se comporta de alguna manera como una referencia rvalue en C ++ 03 y en realidad es una referencia rvalue (es decir, una T&&
) para C ++ 11. No se ocupará del todo de las referencias de r ++ valor de C ++ 03 pero, al menos, permite que los constructores de movimiento y las asignaciones de movimiento se definan sin que realmente se necesiten referencias de valor de r.
Tenga en cuenta que mt::move()
solo se usa para mostrar más adelante cómo rvalue_reference<T>
se puede mover incluso en C ++ 03. El punto principal es que rvalue_reference<T>
es algo que un compilador de C ++ 03 entiende o T&&
. Para esta notación bastante razonable es necesario que el compilador soporte las plantillas de alias. Si ese no es el caso, se puede aplicar el mismo truco pero utilizando un tipo anidado adecuado de una plantilla de clase correspondiente.
Aquí hay un ejemplo de uso de este encabezado:
#include "movetools.hpp"
#include <iostream>
class foo
{
public:
foo() { std::cout << "foo::foo()/n"; }
foo(foo const&) { std::cout << "foo::foo(const&)/n"; }
foo(mt::rvalue_reference<foo> other) {
std::cout << "foo::foo(&&)/n";
this->swap(other);
}
~foo() { std::cout << "foo::~foo()/n"; }
foo& operator= (foo const& other) {
std::cout << "foo::operator=(foo const&)/n";
foo(other).swap(*this);
return *this;
}
foo& operator= (mt::rvalue_reference<foo> other) {
std::cout << "foo::operator=(foo&&)/n";
this->swap(other);
return *this;
}
void swap(foo&) {
std::cout << "foo::swap(foo&)/n";
}
};
int main()
{
foo f0;
foo f1 = f0;
foo f2 = mt::move(f0);
f1 = f2;
f0 = mt::move(f1);
}
Es decir, la lógica de negocios real está exenta de cualquier piratería del preprocesador. La única necesidad de solucionar el problema con el preprocesador se encuentra en el encabezado movetools.hpp
que no necesita movetools.hpp
. Es decir, realmente creo que no usa hacks de preprocesador para definir el constructor de movimiento real o la asignación de movimiento, aunque el preprocesador se usa en algún lugar. Si insiste en que no desea utilizar la piratería de macros, puede hacerlo dirigiendo al compilador para que vea los encabezados diferentes, pero eso es un detalle de implementación de movetools.hpp
.