Trabajo en una gran aplicación de software que debe ejecutarse en varias plataformas. Algunas de estas plataformas admiten algunas características de C ++ 11 (por ejemplo, MSVS 2010) y otras no admiten ninguna (por ejemplo, GCC 4.3.x). Espero que esta situación continúe por varios años (mi mejor estimación: 3-5 años).
Dado eso, me gustaría configurar una interfaz de compatibilidad para que (en la medida de lo posible) las personas puedan escribir código C ++ 11 que aún se compilará con compiladores más antiguos con un mínimo de mantenimiento. En general, el objetivo es minimizar # ifdef's tanto como sea razonablemente posible mientras se habilita la sintaxis / características básicas de C ++ 11 en las plataformas que las admiten y proporcionar emulación en las plataformas que no lo hacen.
Comencemos con std :: move (). La forma más obvia de lograr compatibilidad sería colocar algo como esto en un archivo de encabezado común:
#if !defined(HAS_STD_MOVE)
namespace std { // C++11 emulation
template <typename T> inline T& move(T& v) { return v; }
template <typename T> inline const T& move(const T& v) { return v; }
}
#endif // !defined(HAS_STD_MOVE)
Esto permite a las personas escribir cosas como
std::vector<Thing> x = std::move(y);
... con impunidad. Hace lo que quieren en C ++ 11 y lo hace lo mejor que puede en C ++ 03. Cuando finalmente eliminamos el último compilador de C ++ 03, este código puede permanecer tal cual.
Sin embargo, según el estándar, es ilegal inyectar nuevos símbolos en el std
espacio de nombres. Esa es la teoria. Mi pregunta es: en términos prácticos, ¿hay algún daño en hacer esto como una forma de lograr la compatibilidad hacia adelante?
Respuestas:
He estado trabajando durante un buen tiempo para mantener un nivel de compatibilidad hacia adelante y hacia atrás en mis programas C ++, hasta que finalmente tuve que hacer un kit de herramientas de la biblioteca , que
estoy preparando para el lanzamientoya se ha lanzado. En general, siempre que acepte que no obtendrá una compatibilidad hacia adelante "perfecta" ni en las características (algunas cosas simplemente no se pueden emular hacia adelante) no en la sintaxis (probablemente tendrá que usar macros, espacios de nombres alternativos para algunas cosas) entonces ya está todo listo.Hay una gran cantidad de características que se pueden emular en C ++ 03 en un nivel que es suficiente para un uso práctico, y sin todas las molestias que conlleva, por ejemplo: Boost. Diablos, incluso la propuesta de estándares de C ++
nullptr
sugiere un backport C ++ 03. Y luego está TR1, por ejemplo, para todo lo relacionado con C ++ 11, pero hemos tenido previsualizaciones durante años. ¡No solo eso, algunas características de C ++ 14 como variantes de aserción, functores transparentes yoptional
se pueden implementar en C ++ 03!Las dos únicas cosas que sé que no se pueden respaldar son las plantillas constexpr y variadic.
Con respecto a todo el asunto de agregar cosas al espacio de nombres
std
, mi opinión es que no importa , en absoluto. Piense en Boost, una de las bibliotecas de C ++ más importantes y relevantes, y su implementación de TR1: Boost.Tr1. Si desea mejorar C ++, hágalo compatible con C ++ 11, luego, por definición, lo está convirtiendo en algo que no es C ++ 03, por lo que bloquearse a sí mismo en un Estándar que tiene la intención de evitar o dejar atrás de todos modos es En pocas palabras, contraproducente. Los puristas se quejarán, pero por definición uno no debe preocuparse por ellos.Por supuesto, solo porque no seguirás el Estándar (03) después de todo no significa que no puedas intentarlo, o que irás alegremente rompiéndolo. Ese no es el punto. Siempre y cuando mantenga un control muy cuidadoso de lo que se agrega al
std
espacio de nombres y controle los entornos en los que se utiliza su software (es decir, ¡haga las pruebas!), No debería haber ningún daño irremediable. Si es posible, defina todo en un espacio de nombres separado y solo agregueusing
directivas al espacio de nombresstd
para que no agregue nada más allá de lo que "absolutamente" debe incluir. Lo cual, IINM, es más o menos lo que hace Boost.TR1.Actualización (2013) : como solicitud de la pregunta original y viendo algunos de los comentarios que no puedo agregar debido a la falta de representación, aquí hay una lista de características de C ++ 11 y C ++ 14 y su grado de portabilidad a C ++ 03:
nullptr
: completamente implementable dado el respaldo oficial del Comité; probablemente también tendrá que proporcionar algunas especializaciones de type_traits para que se reconozca como un tipo "nativo".forward_list
: completamente implementable, aunque el soporte del asignador se basa en lo que puede proporcionar su implicación Tr1.vector<int> v = {1, 2, 3, 4};
: completamente implementable, aunque más extenso de lo que uno quisiera.static_assert
: casi completamente implementable cuando se implementa como una macro (solo tendrá que tener cuidado con las comas).unique_ptr
: casi completamente implementable, pero también necesitará soporte del código de llamada (para almacenarlos en contenedores, etc.); Ver el siguiente sin embargo.static_cast<>
podría ser casi imposible.noexcept
: depende de las características de su compilador.auto
semántica ydecltype
: depende de las características del compilador - por ejemplo .:__typeof__
.int16_t
, etc.): depende de las características de su compilador, o puede delegar en el stdint.h portátil.::type
en plantillas para siempreconstexpr
: No se puede implementar a mi entender.dynarray
: totalmente implementable.optional<>
: casi completamente implementable siempre que su compilador C ++ 03 admita configuraciones de alineación.std::less<void>
para que funcione.assure
): completamente implementable si desea afirmaciones, casi completamente implementable si desea habilitar tiros en su lugar.(Descargo de responsabilidad: varias de estas características se implementan en mi biblioteca de backports C ++ que he vinculado anteriormente, por lo que creo que sé de lo que estoy hablando cuando digo "completamente" o "casi completamente").
fuente
Esto es fundamentalmente imposible. Considere
std::unique_ptr<Thing>
. Si fuera posible emular referencias rvalue como una biblioteca, no sería una característica del lenguaje.fuente
std::unique_ptr
allí, pero hay algunas otras características de las referencias rvalue que no se pueden implementar en C ++ 03, porstd::forward
lo que no es posible. La otra cosa es questd::unique_ptr
no será útil, porque las colecciones no usarán la semántica de movimiento a menos que las reemplace todas.unique_ptr
. Mira las fallas deauto_ptr
.unique_ptr
es prácticamente el ejemplo de libro de texto de una clase cuya semántica fue fundamentalmente habilitada por la función del lenguaje.unique_ptr
que haya sido habilitado fundamentalmente por la función de idioma. Sin embargo, no sería muy útil sin esa característica. porque sin reenvío perfecto no sería utilizable en muchos casos y el reenvío perfecto requiere esa característica.-std=c++0x
opción para habilitarlas).std
espacio de nombres es un "comportamiento indefinido". Eso significa que la especificación no dice lo que sucederá. Pero si sabe que en una plataforma en particular la biblioteca estándar no define algo, simplemente continúe y defínalo. Solo espere que tenga que verificar en cada plataforma lo que necesita y lo que puede definir.unique_ptr
. Sin embargo, no sería demasiado útil, ya que depende de colecciones que realmente usan la semántica de movimiento y las de C ++ 03 obviamente no lo harán.fuente