What Differences, If Any, Between C++03 and C++11 Can Be Detected at Run-Time

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

Can C++ code be valid in both C++03 and C++11 but do different things?

The answer is a definite yes. On the plus side there is:

  • Code that previously implicitly copied objects will now implicitly move them when possible.

On the negative side, several examples are listed in the appendix C of the standard. Even though there are many more negative ones than positive, each one of them is much less likely to occur.

String literals

#define u8 "abc"
const char* s = u8"def"; // Previously "abcdef", now "def"

and

#define _x "there"
"hello "_x // Previously "hello there", now a user defined string literal

Type conversions of 0

In C++11, only literals are integer null pointer constants:

void f(void *); // #1
void f(...); // #2
template<int N> void g() {
f(0*N); // Calls #2; used to call #1
}

Rounded results after integer division and modulo

In C++03 the compiler was allowed to either round towards 0 or towards negative infinity. In C++11 it is mandatory to round towards 0

int i = (-1) / 2; // Might have been -1 in C++03, is now ensured to be 0

Whitespaces between nested template closing braces >> vs > >

Inside a specialization or instantiation the >> might instead be interpreted as a right-shift in C++03. This is more likely to break existing code though: (from http://gustedt.wordpress.com/2013/12/15/a-disimprovement-observed-from-the-outside-right-angle-brackets/)

template< unsigned len > unsigned int fun(unsigned int x);
typedef unsigned int (*fun_t)(unsigned int);
template< fun_t f > unsigned int fon(unsigned int x);

void total(void) {
// fon<fun<9> >(1) >> 2 in both standards
unsigned int A = fon< fun< 9 > >(1) >>(2);
// fon<fun<4> >(2) in C++03
// Compile time error in C++11
unsigned int B = fon< fun< 9 >>(1) > >(2);
}

Operator new may now throw other exceptions than std::bad_alloc

struct foo { void *operator new(size_t x){ throw std::exception(); } }
try {
foo *f = new foo();
} catch (std::bad_alloc &) {
// c++03 code
} catch (std::exception &) {
// c++11 code
}

User-declared destructors have an implicit exception specification
example from What breaking changes are introduced in C++11?

struct A {
~A() { throw "foo"; } // Calls std::terminate in C++11
};
//...
try {
A a;
} catch(...) {
// C++03 will catch the exception
}

size() of containers is now required to run in O(1)

std::list<double> list;
// ...
size_t s = list.size(); // Might be an O(n) operation in C++03

std::ios_base::failure does not derive directly from std::exception anymore

While the direct base-class is new, std::runtime_error is not. Thus:

try {
std::cin >> variable; // exceptions enabled, and error here
} catch(std::runtime_error &) {
std::cerr << "C++11\n";
} catch(std::ios_base::failure &) {
std::cerr << "Pre-C++11\n";
}

What is the difference between C++03 `throw()` specifier and C++11 `noexcept`?

Exception specifiers were deprecated because exception specifiers are generally a terrible idea. noexcept was added because it's the one reasonably useful use of an exception specifier: knowing when a function won't throw an exception. Thus it becomes a binary choice: functions that will throw and functions that won't throw.

noexcept was added rather than just removing all throw specifiers other than throw() because noexcept is more powerful. noexcept can have a parameter which compile-time resolves into a boolean. If the boolean is true, then the noexcept sticks. If the boolean is false, then the noexcept doesn't stick and the function may throw.

Thus, you can do something like this:

struct<typename T>
{
void CreateOtherClass() { T t{}; }
};

Does CreateOtherClass throw exceptions? It might, if T's default constructor can. How do we tell? Like this:

struct<typename T>
{
void CreateOtherClass() noexcept(is_nothrow_default_constructible<T>::value) { T t{}; }
};

Thus, CreateOtherClass() will throw iff the given type's default constructor throws. This fixes one of the major problems with exception specifiers: their inability to propagate up the call stack.

You can't do this with throw().

What are the incompatible differences between C(99) and C++(11)?

If you start from the common subset of C and C++, sometimes called clean C (which is not quite C90), you have to consider 3 types of incompatibilities:

  1. Additional C++ featues which make legal C illegal C++

    Examples for this are C++ keywords which can be used as identifiers in C or conversions which are implicit in C but require an explicit cast in C++.

    This is probably the main reason why Microsoft still ships a C frontend at all: otherwise, legacy code that doesn't compile as C++ would have to be rewritten.

  2. Additional C features which aren't part of C++

    The C language did not stop evolving after C++ was forked. Some examples are variable-length arrays, designated initializers and restrict. These features can be quite handy, but aren't part of any C++ standard, and some of them will probably never make it in.

  3. Features which are available in both C and C++, but have different semantics

    An example for this would be the linkage of const objects or inline functions.

A list of incompatibilities between C99 and C++98 can be found here (which has already been mentioned by Mat).

While C++11 and C11 got closer on some fronts (variadic macros are now available in C++, variable-length arrays are now an optional C language feature), the list of incompatibilities has grown as well (eg generic selections in C and the auto type-specifier in C++).

As an aside, while Microsoft has taken some heat for the decision to abandon C (which is not a recent one), as far as I know no one in the open source community has actually taken steps to do something about it: It would be quite possible to provide many features of modern C via a C-to-C++ compiler, especially if you consider that some of them are trivial to implement. This is actually possible right now using Comeau C/C++, which does support C99.

However, it's not really a pressing issue: Personally, I'm quite comfortable with using GCC and Clang on Windows, and there are proprietary alternatives to MSVC as well, eg Pelles C or Intel's compiler.

C++03. Test for rvalue-vs-lvalue at compile-time, not just at runtime

It took some effort, but here's a tested and working is_lvalue macro that correctly handles const struct S function return types. It relies on const struct S rvalues not binding to const volatile struct S&, while const struct S lvalues do.

#include <cassert>

template <typename T>
struct nondeducible
{
typedef T type;
};

char (& is_lvalue_helper(...))[1];

template <typename T>
char (& is_lvalue_helper(T&, typename nondeducible<const volatile T&>::type))[2];

#define is_lvalue(x) (sizeof(is_lvalue_helper((x),(x))) == 2)

struct S
{
int i;
};

template <typename T>
void test_()
{
T a = {0};
T& b = a;
T (* c)() = 0;
T& (* d)() = 0;
assert (is_lvalue(a));
assert (is_lvalue(b));
assert (!is_lvalue(c()));
assert (is_lvalue(d()));
}

template <typename T>
void test()
{
test_<T>();
test_<const T>();
test_<volatile T>();
test_<const volatile T>();
}

int main()
{
test<int>();
test<S>();
}

Edit: unnecessary extra parameter removed, thanks Xeo.

Edit again: As per the comments, this works with GCC but relies on unspecified behaviour in C++03 (it's valid C++11) and fails some other compilers. Extra parameter restored, which makes it work in more cases. const class rvalues give a hard error on some compilers, and give the correct result (false) on others.



Related Topics



Leave a reply



Submit