C and C++ Standardese for "Don't count on it." Compiler vendors gain competitive advantage over each other by making their compilers faster, or more efficient. In order to do this, their compilers perform various optimizations while compiling code; these optimizations are trade secrets by necessity. In other cases, there is no meaningful behavior for a certain situation but the program must work anyway.

Because of this, the C and C++ language standards, written primarily by compiler vendors, describe situations where compiler writers must decide on a particular behavior, but need not document that behavior, or even make it consistent. Such situations are labeled 'Unspecified' within the standards.

Unspecified behavior is an extremely loose requirement for a compiler vendor to provide a behavior; when you cause an unspecified behavior, it is better than undefined behavior, but worse than implementation-defined behavior or the behavior required by the Standard.

You will encounter some unspecified behaviors every day; others are a bit esoteric, meaningful only to language lawyers. You can occasionally write little test programs to reverse engineer these behaviors, but they will almost certainly change the moment you try to port your code to a different compiler (or a new version of the same compiler). It's possible that the behaviors will change the next time you run your program!

Unspecified behaviors in C++ include:

  • Sequencing behaviors:
    • The order of evaluation of arguments to a function.
    • The order of evaluation of subexpressions of an expression, when using operators that do not generate sequence points:
      a + b + c // unspecified order
      a || b || c // must be evaluated in sequence
    • In a new expression, whether the memory is allocated before or after the evaluation of the constructor arguments. If the memory allocation fails, the arguments may or may not be evaluated.
    • The order of evaluation of # and ## in the preprocessor.
    • Whenever expressions with side effects are evaluated in an unspecified order (all cases above), the side effects happen in an unspecified order, as well.
    • The value of a static global object initialized with another static global object declared later.
    • When a POD object is initialized with a brace enclosed initializer list, and some of the initializer expressions are constant and others are not, when the constant expressions are initilized is unspecified. In:
      char *a = { 'a', 'b', getchar()}; 
      getchar() needs to be called during the program, so 'a' and 'b' may or may not be assigned during program initialization.
    • values of objects when a signal interrupts the program, other than those of type volatile sig_atomic_t
    • Whether virtual base classes are assigned more than once by a compiler-generated copy assignment operator. That is, always write your own.
  • Memory behaviors:
    • Whether references take up space or not (
    • The amount of overhead consumed by memory allocation.
    • Where the memory for temporary copies of exceptions being thrown comes from;
    • The order of the blocks of memory allocated in different dynamic allocation operations (see pointer comparison below).
    • The order in memory of the nonstatic data members of a class seperated by access specifiers (see pointer comparison below).
    • The order in memory of the base class subobjects of a derived class object.
    • The order in the code segment of function bodies (and thus the order of function pointers).
  • Standard library behaviors:
    • In general, the implementations of all of the Standard Library classes.
    • The meaning of standard allocator hints, if any.
    • The values stored within type_info objects.
    • Whether the type_info object returned by a typeid expression is ever destroyed (which can only happen when the program terminates).
    • Whether C standard library functions have extern "C" or extern "C++" linkage in C++.
    • Whether classes in the C++ Standard library define the member functions that the compiler can generate by default.
    • Whether library functions (globals or class members) are defined inline or not.
    • Whether any library functions or classes are friends of other library classes or not.
    • Whether any library classes are base classes or derived from other classes.
    • The type of a member function pointer for a library class.
    • Whether or not library functions use C library functions (most importantly, the default allocation functions may or may not use the C function malloc).
    • In a char_traits specialization, the value of char_traits::to_char_type (i) or char_traits::eq_int_type (i, c)for some integer value i that does not match any underlying integer value for the character type.
    • The capacity() of a basic_string just after construction. In other words, assign one a value before using it.
    • The return value of time_get when passed an incorrect format.
    • The result of instantiating std::complex<> with anything other than float, double, or long double.
    • The order of elements in an return value of time_get when passed an incorrect format.
  • Template behaviors:
  • Conversion behaviors:
    • The value of a pointer object converted to another pointer type (except that if it's converted back, you get the same value back).
      int *i = new int ;
      char *j = static_cast<char *>(i) // j is unspecified
      int *k = static_cast<int *>(j) // k == i
    • The underlying integral type of an enumerator if it or the one preceding it has no initializer:
      enum e1 { a = (unsigned long) 0, b, c}; // unsigned long
      enum e2 { a, b, c}; // unspecified
      enum e1 { a, b = (unsigned long) 0, c}; // a is unspecified type,
                                                 others are unsigned long
    • the value of an object of an enumeration type assigned a value outside the enumeration's range.
      enum e {a = 1, b = 2, c = 3};
      e e0;
      e0 = static_cast<e>(4); // unspecified (
    • When using C-style casting syntax to convert a pointer to an incomplete type to a pointer to another incomplete type, whether it's interpreted as a static_cast or a reinterpret_cast.
      class A;
      class B;
      B *bstar(A *a)
       return (B *)(a); // unspecified; better to explicitly 
                           static_cast or reinterpret_cast
  • Pointer comparison behaviors:
    • The result of comparing pointers to virtual member functions using == or !=.
    • The result of comparing two pointers of compatible types using ordering operators <,<=,>,>=. except in a few circumstances:
      • If they point to data members of the same object (not seperated by an access specifier)
      • If they point to elements of the same array;
      • If they point to the same object or function
      • if they are both null or both non-null.
      class C
       int a;
       int b;
       int c;
       int *pa = &a;
       int *pb = &b;
       int *pc = &c;
       pa <= pb; // OK
       pa <= pb; // unspecified
       pa = new int [20];
       pb = new int [20];
       pc = a+15;
       pa <= pb;     // unspecified
       pa <= pc;     // OK (true)
       pc = a+20;    // one past the end
       pa <= pc;     // unspecified
       a + 20 <= pc; // OK
       pc = 0;
       pa <= pc;     // unspecified
    • The value of a member or subobject of a const object under construction, not accessed through the constructor's this pointer.
      class C;
      void foo (C *c);
      C o;
      struct C 
       int m;
       C(): m (0) { foo (this); }
      void foo (C *p)
       cout << p -> m; // OK, accessed through constructor's this
       cout << o.m;    // unspecified in constructor's call to foo
       p -> m = 1;     // OK
       cout << o.m;    // STILL unspecified

    Left out:,

    Source: International Standard ISO-IEC 14882, Programming Languages -- C++, published by the American National Standards Institute, © 1998 Information Technology Industry Council.

Log in or register to write something here or to contact authors.