informacion - for c++ 17
¿Qué diferencias, si las hay, entre C++ 03 y C++ 11 pueden detectarse en tiempo de ejecución? (8)
Idioma principal
Accediendo a un enumerador usando ::
template<int> struct int_ { };
template<typename T> bool isCpp0xImpl(int_<T::X>*) { return true; }
template<typename T> bool isCpp0xImpl(...) { return false; }
enum A { X };
bool isCpp0x() {
return isCpp0xImpl<A>(0);
}
También puede abusar de las nuevas palabras clave
struct a { };
struct b { a a1, a2; };
struct c : a {
static b constexpr (a());
};
bool isCpp0x() {
return (sizeof c::a()) == sizeof(b);
}
Además, el hecho de que los literales de cadena ya no se conviertan en caracteres char*
bool isCpp0xImpl(...) { return true; }
bool isCpp0xImpl(char*) { return false; }
bool isCpp0x() { return isCpp0xImpl(""); }
Sin embargo, no sé qué tan probable es que tengas esto trabajando en una implementación real. Uno que explota auto
struct x { x(int z = 0):z(z) { } int z; } y(1);
bool isCpp0x() {
auto x(y);
return (y.z == 1);
}
Lo siguiente se basa en el hecho de que el operator int&&
es una función de conversión para int&&
en C ++ 0x, y una conversión a int
seguida de lógica, y en C ++ 03
struct Y { bool x1, x2; };
struct A {
operator int();
template<typename T> operator T();
bool operator+();
} a;
Y operator+(bool, A);
bool isCpp0x() {
return sizeof(&A::operator int&& +a) == sizeof(Y);
}
Ese caso de prueba no funciona para C ++ 0x en GCC (parece un error) y no funciona en el modo C ++ 03 para clang. Se ha registrado un PR de clang .
El tratamiento modificado de los nombres de clase inyectados de plantillas en C ++ 11:
template<typename T>
bool g(long) { return false; }
template<template<typename> class>
bool g(int) { return true; }
template<typename T>
struct A {
static bool doIt() {
return g<A>(0);
}
};
bool isCpp0x() {
return A<void>::doIt();
}
Un par de "detectar si se trata de C ++ 03 o C ++ 0x" se puede utilizar para demostrar los cambios bruscos. El siguiente es un caso de prueba ajustado, que inicialmente se usó para demostrar dicho cambio, pero ahora se usa para probar C ++ 0x o C ++ 03.
struct X { };
struct Y { X x1, x2; };
struct A { static X B(int); };
typedef A B;
struct C : A {
using ::B::B; // (inheriting constructor in c++0x)
static Y B(...);
};
bool isCpp0x() { return (sizeof C::B(0)) == sizeof(Y); }
Biblioteca estándar
Detectando la falta de operator void*
del operator void*
en C ++ 0x '' std::basic_ios
struct E { E(std::ostream &) { } };
template<typename T>
bool isCpp0xImpl(E, T) { return true; }
bool isCpp0xImpl(void*, int) { return false; }
bool isCpp0x() {
return isCpp0xImpl(std::cout, 0);
}
Es posible escribir una función que, cuando se compila con un compilador de C, devolverá 0, y cuando se compila con un compilador de C ++, devolverá 1 (la separación trivial con #ifdef __cplusplus
no es interesante).
Por ejemplo:
int isCPP()
{
return sizeof(char) == sizeof ''c'';
}
Por supuesto, lo anterior solo funcionará si sizeof (char)
no es lo mismo que sizeof (int)
Otra solución más portátil es algo como esto:
int isCPP()
{
typedef int T;
{
struct T
{
int a[2];
};
return sizeof(T) == sizeof(struct T);
}
}
No estoy seguro de si los ejemplos son correctos al 100%, pero entiendes la idea. Creo que hay otras formas de escribir la misma función también.
¿Qué diferencias, si las hay, entre C ++ 03 y C ++ 11 pueden detectarse en tiempo de ejecución? En otras palabras, ¿es posible escribir una función similar que devuelva un valor booleano que indique si está compilado por un compilador C ++ 03 conforme o un compilador C ++ 11?
bool isCpp11()
{
//???
}
¿Qué tal un cheque usando las nuevas reglas para >>
cerrar plantillas?
#include <iostream>
const unsigned reallyIsCpp0x=1;
const unsigned isNotCpp0x=0;
template<unsigned>
struct isCpp0xImpl2
{
typedef unsigned isNotCpp0x;
};
template<typename>
struct isCpp0xImpl
{
static unsigned const reallyIsCpp0x=0x8000;
static unsigned const isNotCpp0x=0;
};
bool isCpp0x() {
unsigned const dummy=0x8000;
return isCpp0xImpl<isCpp0xImpl2<dummy>>::reallyIsCpp0x > ::isNotCpp0x>::isNotCpp0x;
}
int main()
{
std::cout<<isCpp0x()<<std::endl;
}
Alternativamente, una comprobación rápida para std::move
:
struct any
{
template<typename T>
any(T const&)
{}
};
int move(any)
{
return 42;
}
bool is_int(int const&)
{
return true;
}
bool is_int(any)
{
return false;
}
bool isCpp0x() {
std::vector<int> v;
return !is_int(move(v));
}
A diferencia de C ++ anterior, C ++ 0x permite crear tipos de referencia a partir de tipos de referencia si ese tipo de referencia base se introduce a través, por ejemplo, de un parámetro de plantilla:
template <class T> bool func(T&) {return true; }
template <class T> bool func(...){return false;}
bool isCpp0x()
{
int v = 1;
return func<int&>(v);
}
El envío perfecto tiene el precio de romper la compatibilidad hacia atrás, desafortunadamente.
Otra prueba podría basarse en los tipos locales ahora permitidos como argumentos de plantilla:
template <class T> bool cpp0X(T) {return true;} //cannot be called with local types in C++03
bool cpp0X(...){return false;}
bool isCpp0x()
{
struct local {} var;
return cpp0X(var);
}
Aunque no es tan conciso ... En el C ++ actual, el nombre de la plantilla de clase en sí mismo se interpreta como un nombre de tipo (no un nombre de plantilla) en el alcance de esa plantilla de clase. Por otro lado, el nombre de la plantilla de clase se puede usar como nombre de plantilla en C ++ 0x (N3290 14.6.1 / 1).
template< template< class > class > char f( int );
template< class > char (&f(...))[2];
template< class > class A {
char i[ sizeof f< A >(0) ];
};
bool isCpp0x() {
return sizeof( A<int> ) == 1;
}
De esta pregunta :
struct T
{
bool flag;
T() : flag(false) {}
T(const T&) : flag(true) {}
};
std::vector<T> test(1);
bool is_cpp0x = !test[0].flag;
Este no es un ejemplo bastante correcto, pero es un ejemplo interesante que puede distinguir C vs. C ++ 0x (sin embargo, es C ++ 03 no válido):
int IsCxx03()
{
auto x = (int *)0;
return ((int)(x+1) != 1);
}
Me inspiré en ¿Qué cambios de ruptura se introducen en C ++ 11? :
#define u8 "abc"
bool isCpp0x() {
const std::string s = u8"def"; // Previously "abcdef", now "def"
return s == "def";
}
Esto se basa en los nuevos literales de cadena que tienen prioridad sobre la expansión de macros.
#include <utility>
template<typename T> void test(T t) { t.first = false; }
bool isCpp0x()
{
bool b = true;
test( std::make_pair<bool&>(b, 0) );
return b;
}