Quais diferenças, se houver, entre C ++ 03 e C ++ 11 podem ser detectadas em tempo de execução?

116

É possível escrever uma função que, quando compilada com um compilador C retornará 0, e quando compilada com um compilador C ++, retornará 1 (a solução trivial com #ifdef __cplusplusnão é interessante).

Por exemplo:

int isCPP()
{
    return sizeof(char) == sizeof 'c';
}

Claro, o acima só funcionará se sizeof (char)não for o mesmo quesizeof (int)

Outra solução mais portátil é algo assim:

int isCPP()
{
    typedef int T;
    {
       struct T 
       {
           int a[2];
       };
       return sizeof(T) == sizeof(struct T);
    }
}

Não tenho certeza se os exemplos estão 100% corretos, mas você entendeu. Eu acredito que existem outras maneiras de escrever a mesma função também.

Quais diferenças, se houver, entre C ++ 03 e C ++ 11 podem ser detectadas em tempo de execução? Em outras palavras, é possível escrever uma função semelhante que retornaria um valor booleano indicando se ela foi compilada por um compilador C ++ 03 em conformidade ou um compilador C ++ 11?

bool isCpp11()
{ 
    //???
} 
Armen Tsirunyan
fonte
10
E qual é o objetivo deste exercício? Em primeiro lugar, você tem uma macro e, em segundo lugar, levará alguns anos antes que os compiladores comecem a implementar todos os recursos do C ++ 0x; enquanto isso, será uma mistura. Portanto, o único teste razoável é o compilador de uma macro de versão.
Gene Bushuyev
4
Esta não é uma pergunta real, mas parece muito interessante seguir as regras!
David Heffernan
4
@Gene et al: Você não vota todas as perguntas que são interessantes, mas não vê o "ponto" pragmático?
Armen Tsirunyan
2
“Esperamos que as respostas geralmente envolvam fatos, referências ou conhecimentos específicos”. Acho que essa questão atende a essas expectativas, vote para reabrir.
Karel Petranek
6
@sixlettervariables: embora esteja certamente aberto ao argumento de que o fraseado poderia ser melhor, parece-me que a noção fundamental da questão (quais diferenças, se houver, entre C ++ 03 e C ++ 0x podem ser detectadas na execução- tempo?) é perfeitamente legítimo. Dado que o código deve ser compilado e executado em ambos, também poderia ser formulado como sendo sobre as alterações significativas em C ++ 0x. Essa também me parece uma pergunta perfeitamente legítima.
Jerry Coffin

Respostas:

108

Linguagem central

Acessando um 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);
}

Você também pode abusar das novas palavras-chave

struct a { };
struct b { a a1, a2; };

struct c : a {
  static b constexpr (a());
};

bool isCpp0x() {
  return (sizeof c::a()) == sizeof(b);
}

Além disso, o fato de que literais de string não são mais convertidos para char*

bool isCpp0xImpl(...) { return true; }
bool isCpp0xImpl(char*) { return false; }

bool isCpp0x() { return isCpp0xImpl(""); }

Não sei qual é a probabilidade de você ter isso funcionando em uma implementação real. Aquele que exploraauto

struct x { x(int z = 0):z(z) { } int z; } y(1);

bool isCpp0x() {
  auto x(y);
  return (y.z == 1);
}

O seguinte é baseado no fato de que operator int&&é uma função de conversão para int&&em C ++ 0x e uma conversão para intseguida por lógico e em 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);
}

Esse caso de teste não funciona para C ++ 0x no GCC (parece um bug) e não funciona no modo C ++ 03 para clang. Um clang PR foi arquivado .

O tratamento modificado de nomes de classes injetadas de modelos em 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();
}

Um par de "detectar se este é C ++ 03 ou C ++ 0x" pode ser usado para demonstrar alterações significativas. A seguir está um caso de teste ajustado, que inicialmente foi usado para demonstrar tal mudança, mas agora é usado para testar C ++ 0x ou 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 Padrão

Detectando a falta de operator void*em 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);
}
Johannes Schaub - litb
fonte
1
Agradável. Posso confirmar que esta solução funciona aqui com g ++ (GCC) 4.6.0, com e sem -std = c ++ 0x.
Alexander
2
Isso retorna truepara o MSVC 2005 em diante e um erro de compilação no MSVC 2003.
Anthony Williams
1
Nossa, eles quebraram a compatibilidade com versões anteriores!
avakar
14
@Johannes: Esta é a maior diversão que você já teve nas últimas semanas, não é? ; -]
ildjarn
4
Acho tudo isso muito interessante, mas acho que o mais inteligente são os calls (...)vs. (char*)Eu realmente gosto daquilo!
corsiKa
44

Eu me inspirei em Quais mudanças importantes foram introduzidas no C ++ 11? :

#define u8 "abc"

bool isCpp0x() {
   const std::string s = u8"def"; // Previously "abcdef", now "def"
   return s == "def";
}

Isso se baseia nos novos literais de string que têm precedência sobre a expansão da macro.

Karel Petranek
fonte
1
+1: Muito interessante, de fato, mas tecnicamente quebra o requisito de não usar o pré-processador. Mas a restrição não tinha a intenção de descartar respostas tão boas :)
Armen Tsirunyan
1
Bem, se você seguir a função com um, #undef u8então o uso do pré-processador só será observável se seu programa tiver uma macro definida anteriormente chamada u8(boooo). Se essa for uma preocupação real, ainda pode ser contornada usando pragmas / chamadas de macro push / pop específicos de implementação (acredito que a maioria das implementações tem isso).
James McNellis
3
Um argumento bastante razoável é que em um sistema C ++ 03 alguém pode #define u8 para fornecer recursos C ++ 0x simulados. Mesmo assim, gosto muito da resposta.
Christopher Smith
1
você pode simplesmente mover esta função isCpp0x para uma unidade de tradução separada para que essa macro não afete outro código.
unkulunkulu
1
Acho que há uma diferença entre usar o pré-processador para contar com a configuração de algum valor macro do compilador e usar o pré-processador para detectar recursos reais da linguagem. É por isso que não acho que essa resposta seja trapaça.
Lightness Races in Orbit
33

Que tal uma verificação usando as novas regras para >>modelos de fechamento:

#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;
}

Como alternativa, uma verificação 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));
}
Anthony Williams
fonte
6
+1 Uma idéia legal, de fato :) No entanto, na prática isso vai quebrar com o Visual C ++ 2005/2088, que não suporta C ++ 0x, mas permite >> ser usado em modelos da maneira C ++ 0x.
Karel Petranek
4
Oooh; Eu gosto do abuso ADL! No entanto, uma implementação C ++ 03 em conformidade não poderia ter uma função chamada std::move?
James McNellis
1
@FredOverflow: Eu não me incomodaria. A IU é uma merda!
Lightness Races in Orbit
16

Ao contrário do C ++ anterior, C ++ 0x permite que tipos de referência sejam criados a partir de tipos de referência se esse tipo de referência de base for introduzido por meio, por exemplo, de um parâmetro de modelo:

template <class T> bool func(T&) {return true; } 
template <class T> bool func(...){return false;} 

bool isCpp0x() 
{
    int v = 1;
    return func<int&>(v); 
}

O encaminhamento perfeito tem o preço de quebrar a compatibilidade com versões anteriores, infelizmente.

Outro teste pode ser baseado em tipos locais agora permitidos como argumentos de modelo:

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);
}
Uwedolinsky
fonte
Talvez eu devesse ter transformado isso em uma classe de traço;)
uwedolinsky
1
+1 Boa ideia, só não tenho certeza se isC++0xé um identificador C ++ válido;)
Karel Petranek
1
Qual é uma boa referência para a referência da inferência de referências?
Kerrek SB
@ kerrek-sb: O rascunho fala sobre isso em 8.3.2.6 (Referências)
uwedolinsky
15

Este não é um exemplo totalmente correto, mas é um exemplo interessante que pode distinguir C vs. C ++ 0x (embora seja C ++ 03 inválido):

 int IsCxx03()
 {
   auto x = (int *)0;
   return ((int)(x+1) != 1);
}
Adam Rosenfield
fonte
10
Tecnicamente, isso depende de sizeof(int) != 1ser verdade. Em um sistema 0x com chars excepcionalmente grandes , os resultados podem ser os mesmos. Ainda um truque legal, no entanto.
Dennis Zickefoose
@Dennis - charé sempre um byte
4
@Node: um byte nem sempre é de 8 bits.
Alexandre C.
2
@Node sizeof(char)será sempre 1, por definição. Mas CHAR_BIT(definido em limits.h) pode ter mais de 8. Como resultado, chare intpode ter 32 bits, e nesse caso sizeof(int) == 1(e CHAR_BIT == 32).
Sjoerd
12

A partir desta pergunta :

struct T
{
    bool flag;
    T() : flag(false) {}
    T(const T&) : flag(true) {}
};

std::vector<T> test(1);
bool is_cpp0x = !test[0].flag;
Alexandre C.
fonte
Eu me perguntei como isso poderia funcionar; depois de tentar, está claro agora: há um pequeno bug. funciona se você alterar a última linha para:bool is_cpp0x = !test[0].flag;
awx
1
plausível: construções padrão C ++ 0x Tenquanto construções de cópia C ++ 03 deT()
awx
9

Embora não seja tão conciso ... No C ++ atual, o próprio nome do modelo de classe é interpretado como um nome de tipo (não um nome de modelo) no escopo desse modelo de classe. Por outro lado, o nome do modelo de classe pode ser usado como um nome de modelo em 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;
}
Ise Wisteria
fonte
9
#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;
}
Jonathan Wakely
fonte
NB, tecnicamente, isso testa a biblioteca padrão, não o compilador, e embora seja C ++ 03 válido e C ++ 0x 0x, não é C ++ 98 válido, então com alguns ajustes pode ser feito para detectar um C ++ 98 / C ++ 03 / C ++ 0x stdlib
Jonathan Wakely