What differences, if any, between C++03 and C++11 can be detected at run-time?
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);
}
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.
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));
}