116

It is possible to write a function, which, when compiled with a C compiler will return 0, and when compiled with a C++ compiler, will return 1 (the trivial sulution with #ifdef __cplusplus is not interesting).

For example:

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

Of course, the above will work only if sizeof (char) isn't the same as sizeof (int)

Another, more portable solution is something like this:

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

I am not sure if the examples are 100% correct, but you get the idea. I believe there are other ways to write the same function too.

What differences, if any, between C++03 and C++11 can be detected at run-time? In other words, is it possible to write a similar function which would return a boolean value indicating whether it is compiled by a conforming C++03 compiler or a C++11 compiler?

bool isCpp11()
{ 
    //???
} 
Destructor
  • 13,235
  • 8
  • 48
  • 108
Armen Tsirunyan
  • 120,726
  • 52
  • 304
  • 418
  • Related: [Incompatibilities between ISO C and ISO C++](http://david.tribble.com/text/cdiffs.htm) (no mention of C++0x, unfortunately) – Adam Rosenfield Jun 24 '11 at 20:48
  • 1
    Another Related: http://stackoverflow.com/questions/6399615/what-breaking-changes-are-introduced-in-c11 (What breaking changes are introduced in C++11?). – Karel Petranek Jun 24 '11 at 21:30
  • 10
    And what's the point of this exercise? Firstly, you do have a macro, and secondly it will take a number of years before compilers start implementing all features of C++0x, in the meanwhile it will be a mix. So the only reasonable test is the compiler a version macro. – Gene Bushuyev Jun 25 '11 at 02:34
  • 4
    This fits the bill of not a real question but it seems too interesting to follow the rules! – David Heffernan Jun 25 '11 at 07:23
  • 4
    @Gene et al: Do you downvote all questions which are interesting but you don't see the pragmatic "point"? – Armen Tsirunyan Jun 25 '11 at 10:41
  • 2
    "We expect answers to generally involve facts, references, or specific expertise". I think this question meets these expectations, vote for reopen. – Karel Petranek Jun 26 '11 at 10:59
  • 2
    @six if you look around, many valid questions on SO challenge "folks to write a block of code". Still that doesn't make it codegolf and doesn't make it offtopic. You have it wrong. – Johannes Schaub - litb Jun 26 '11 at 17:13
  • 1
    @six This can't objectively have a winner. And has a fixed limit of valid answers (there is only a fixed variety of incompatibilities introduced). Two things that codegolf questions don't share with it. – Johannes Schaub - litb Jun 26 '11 at 17:21
  • @six: It **can** and does have correct answers. But not **one** correct answer, rather a limited set of correct answers – Armen Tsirunyan Jun 26 '11 at 18:49
  • 1
    @six: That's not true at all. This is a legitimate C++ question which is asking whether it is possible to exploit incompatibility between C++03 and C++0x. But I guess reasoning isn't something that's gonna convince you, so... let's agree to differ here – Armen Tsirunyan Jun 26 '11 at 18:59
  • 6
    @sixlettervariables: while it's certainly open to argument that the phrasing could be better, it seems to me that the fundamental notion of the question (what differences, if any, between C++03 and C++0x can be detected at run-time?) is perfectly legitimate. Given that the code has to compile and execute in both, it could also be phrased as being about the breaking changes in C++0x. That seems to me like a perfectly legitimate question to ask as well. – Jerry Coffin Jun 26 '11 at 19:17
  • 1
    @DanMoulding: That's because, as you could have guessed, the purpose of the question is more about code that works differently in C++11 and C++03 rather than a practical way to detect the compiler version. Duh – Armen Tsirunyan Aug 16 '13 at 12:37
  • @DanMoulding: There was a HUGE debate as to how to word the question. This even migrated to codegolf and back and was discussed at meta. This wording seems to be the consensus the community arrived at – Armen Tsirunyan Aug 16 '13 at 14:10

8 Answers8

108

Core Language

Accessing an enumerator using :::

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

You can also abuse the new keywords

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

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

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

Also, the fact that string literals do not anymore convert to char*

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

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

I don't know how likely you are to have this working on a real implementation though. One that exploits auto

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

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

The following is based on the fact that operator int&& is a conversion function to int&& in C++0x, and a conversion to int followed by logical-and in 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);
}

That test-case doesn't work for C++0x in GCC (looks like a bug) and doesn't work in C++03 mode for clang. A clang PR has been filed.

The modified treatment of injected class names of templates in 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();
}

A couple of "detect whether this is C++03 or C++0x" can be used to demonstrate breaking changes. The following is a tweaked testcase, which initially was used to demonstrate such a change, but now is used to test for C++0x or 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); }

Standard Library

Detecting the lack of operator void* in 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
  • 466,055
  • 116
  • 851
  • 1,175
  • 1
    Nice. I can confirm that this solution works here with g++ (GCC) 4.6.0, both with and without -std=c++0x. – Alexander Jun 24 '11 at 20:17
  • 2
    This returns `true` for MSVC 2005 onwards, and a compile error in MSVC 2003. – Anthony Williams Jun 24 '11 at 20:20
  • 1
    Oh dear, they broke backwards compatibility! – avakar Jun 24 '11 at 20:21
  • Both of the tricks used can return `true` prematurely with compiler extensions – Puppy Jun 24 '11 at 20:45
  • 1
    The `constexpr` test is invalid C++0x, since the function is not defined. The `enum` test returns `false` for MSVC 2010 --- I guess it's not "C++0x"-y enough :-) Nice idea though. – Anthony Williams Jun 24 '11 at 21:45
  • 1
    @Anthony, hmm, the spec allows `constexpr` on function declarations (and has examples for it). I thought that calling such a non-defined declaration just means that the call is not a constant expression (as it matches one of the bullets in 5.19 - "an invocation of an undefined constexpr function or an undefined constexpr constructor"). Is there a rule I missed? It works on GCC. – Johannes Schaub - litb Jun 24 '11 at 21:54
  • 1
    @Johannes: Sorry, my mistake. I forgot that got changed. It works OK in g++ 4.6, but g++ 4.5 complains. – Anthony Williams Jun 24 '11 at 22:10
  • Removed the local classes thing because it's not valid C++03 SFINAE :( – Johannes Schaub - litb Jun 24 '11 at 22:36
  • 14
    @Johannes : This is the most fun you've had in weeks, isn't it? ;-] – ildjarn Jun 24 '11 at 23:09
  • 4
    I find these very all interesting, but I think the most clever is the `(...)` vs `(char*)` calls. I really like that! – corsiKa Jun 26 '11 at 00:23
44

I got an inspiration from What breaking changes are introduced in C++11?:

#define u8 "abc"

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

This is based on the new string literals that take precedence over macro expansion.

Community
  • 1
  • 1
Karel Petranek
  • 14,540
  • 4
  • 39
  • 66
  • 1
    +1: Very interesting, indeed, but technically breaks the requirement of not using the preprocessor. But the restriction was not intended to dismiss such nice answers :) – Armen Tsirunyan Jun 24 '11 at 20:52
  • Yeah, the preprocessor is a bit hacky considering the question. But I couldn't resist :) – Karel Petranek Jun 24 '11 at 21:00
  • 1
    Well, if you follow the function with a `#undef u8` then the preprocessor usage is only observable if your program has a previously-defined macro named `u8` (boooo). If that is a real concern, that can still be worked around using implementation-specific push/pop macro pragmas/calls (most implementations have these, I believe). – James McNellis Jun 24 '11 at 22:33
  • 3
    One fairly reasonable argument is that on a C++03 system someone might #define u8 to provide simulated C++0x capabilities. Still, I really like the answer. – Christopher Smith Jun 26 '11 at 10:55
  • 1
    you can just move this isCpp0x function to a separate translation unit so this macro thing won't affect other code. – unkulunkulu Jul 25 '11 at 14:23
  • 1
    I think there's a difference between using the preprocessor to rely on the compiler setting some macro value, and using the preprocessor to detect actual language features. That's why I don't think that this answer is cheating. – Lightness Races in Orbit Sep 18 '11 at 13:51
33

How about a check using the new rules for >> closing templates:

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

Alternatively a quick check for 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
  • 62,015
  • 12
  • 122
  • 149
16

Unlike prior C++, C++0x allows reference types to be created from reference types if that base reference type is introduced through, for example, a template parameter:

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

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

Perfect forwarding comes at the price of breaking backwards compatibility, unfortunately.

Another test could be based on now-allowed local types as template arguments:

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);
}
Daniel Frey
  • 52,317
  • 11
  • 110
  • 168
uwedolinsky
  • 611
  • 3
  • 8
15

This isn't quite a correct example, but it's an interesting example that can distinguish C vs. C++0x (it's invalid C++03 though):

 int IsCxx03()
 {
   auto x = (int *)0;
   return ((int)(x+1) != 1);
}
Adam Rosenfield
  • 360,316
  • 93
  • 484
  • 571
  • 10
    Technically, this relies on `sizeof(int) != 1` being true. On an 0x system with exceptionally large `char`s, the results could be the same. Still a neat trick, though. – Dennis Zickefoose Jun 24 '11 at 20:29
  • @Dennis - `char` is always one byte though – Node Jun 24 '11 at 23:20
  • 4
    @Node: one byte is not always 8 bits. – Alexandre C. Jun 24 '11 at 23:48
  • 2
    @Node `sizeof(char)` will always be 1, by definition. But `CHAR_BIT` (defined in limits.h) is allowed to be more than 8. As a result, both `char` and `int` could have 32 bits, in which case `sizeof(int) == 1` (and `CHAR_BIT == 32`). – Sjoerd Jun 25 '11 at 01:08
12

From this question:

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

std::vector<T> test(1);
bool is_cpp0x = !test[0].flag;
Community
  • 1
  • 1
Alexandre C.
  • 52,206
  • 8
  • 116
  • 189
  • I wondered how this could work; having tried it, it's clear now: there's a little bug. it works if you change the last line to: `bool is_cpp0x = !test[0].flag;` – awx Aug 26 '11 at 22:41
  • 1
    plausible: C++0x default constructs `T` whereas C++03 copy constructs from `T()` – awx Aug 26 '11 at 22:49
9

Though not so concise... In current C++, class template name itself is interpreted as a type name (not a template name) in that class template's scope. On the other hand, class template name can be used as a template name in 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
  • 10,349
  • 2
  • 37
  • 26
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;
}
  • N.B. technically this tests the standard library not the compiler, and although it is valid C++03 and valid C++0x, it's not valid C++98, so with some tweaks could be made to detect a C++98/C++03/C++0x stdlib – Jonathan Wakely Jun 25 '11 at 00:34