C++ Compiler Tips and Workarounds

This page last
updated on:

May 16, 2001

Contents and Related Links

CodeWrangler Pages

Overview

This page provides workarounds and information on C++ compilers standards conformance problems. This is by no means a comprehensive list, only some of the common problems I've run into from assignments submitted by my students. Most of them are oriented towards Microsoft Visual C++, for a couple of reasons:
  • VC++ is one of the most widely used C++ compilers.
  • Microsoft has historically placed little emphasis on C++ standards conformance, and instead publicly states that providing Windows development tools is their (and their customers) highest priority. There are millions of lines of code written to the current VC++ version, and bringing VC++ into standards conformance would cause a large amount of code to break (including much of Microsoft's own code). (Future VC++ versions under the .NET umbrella are rumored to be closer to the C++ standard.)
There are numerous areas where VC++ is non-conformant, and many of them tend to be in commonly used and useful areas (such as 'for' loop variables). There is a difference between being non-conformant (allowing code that is specifically illegal according to the C++ standard), and not having implemented portions of the C++ standard (which means that legal C++ code would not compile because a feature has not been implemented in the compiler). Regarding 100% compliance, I don't know of any compiler that is there yet - e.g. most compiler still have incomplete template functionality (e.g. areas such as template specialization, partial specialization, template member functions, template template arguments, template default arguments, template function explicit instantiation, and in particular the 'export' keyword for separating template declarations from template implementations, which is a feature I would love to see).

MS is not the only compiler vendor to face backwards compatibility issues when moving towards the C++ standard. Sun C++ 5.0 was a dramatic change for Sun C++ developers, and Gnu C++ (GCC) is going through some of the same 'becoming standards compliant' issues.

It's possible to code a common subset that meets multiple compilers (I currently target Metrowerks CodeWarrior, VC++, and GCC for my teaching solution code), although it takes some effort. I look forward to the day when all C++ compilers provide a solid base of C++ standard conformance, allowing 'workaround' code and '#ifdef' type solutions to be eliminated. I suspect it will still be a number of years before that happens, though.

General Microsoft Visual C++ Tips

There are patches and fixes available for the VC++ standard library (which is provided by Dinkumware) at: http://www.dinkumware.com/ and service packs for the general VC++ environment from Microsoft at: http://msdn.microsoft.com/

(From Karel Blaha:)

VC++ has a compiler command line option that turns off Microsoft extensions to the C/C++ language.  To disable these extensions, go to Projects \ Settings, C/C++ tab and add "/Za" to the Project Options.

For example, the default project settings do not support the new C++ for loop variable scope.  The /Za option enforces the new standard and disallows the older rules.

Note that you cannot use this switch if you compile Win32 API code because Windows headers use some of the non-standard language features.

A note on warnings about long identifiers (also from Karel Blaha):

These warnings should be harmless - what you may lose is some functionality when trying to step through your code in debugger since the symbolic information is truncated.

On the other hand, it is pretty annoying to see 200+ messages every time you compile. I use the following compiler directives to turn these warnings off.

#ifdef _MSC_VER
// turn off symbol length warnings
#pragma warning (disable: 4786)
#pragma warning (disable: 4503)
#endif

(The #ifdef guarantees that the #pragma directives are seen only by Microsoft compilers).

Boolean Functionality

Most compilers now support the standard C++ language bool type, and the true and false keywords. There are numerous sources for reading about how to use these capabilities.

'for' Loop Variable Scope

The C++ standard specifies that the scope of any 'for' loop variables declared in the 'for' loop are for the 'for' loop statement itself. In other words, in standard C++ the lifetime of a variable declared in this manner does not extend past the end of the for loop statement. This is different than many early C++ compilers, which allowed the lifetime of the variable to extend to the end of the enclosing block. In particular MS VC++ still has the older non-standard behavior for 'for' loop variables as a default.

An example with non-standard C++ syntax errors:

for (int i = 0; i < MAX; ++i) {
  // do something
} // end of scope for the variable i, in standard C++
if (i >= MAX) { // syntax error! i is out of scope
// ...
for (i = 0; i < 10; ++i) { // syntax error! i is out of scope
// ...
An example with standard C++ usage:
for (int i = 0; i < MAX; ++i) {
  // do something
} // end of scope for the variable i, in standard C++
for (int i = 0; i < 10; ++i) { // use i again
  // do something else
}
int j; // we need j past the end of the for loop
for (j = 0; j < SZ; ++j) {
  // do whatever
}
if (j >= SZ) { // legal and valid
For portable code between standard C++ and compilers supporting older behavior (such as MS VC++), either use different variable names for each for loop (recommended) and do not access the variable past the end of the loop, or declare the variable before the for loop.

Implicit Declaration of 'int'

The C++ standard does not allow implicit declaration of int. The following is invalid:
const BUF_SZ = 100; // illegal - syntax error!
The correct way (and incidentally a much better style) is:
const int BUF_SZ = 100;

The 'main' Function Must Return 'int'

Returning void from main, or letting main implicitly return an int is non-standard. Declare main to return an int, and then return an integer value from the function.

When 'new'ing an Array, a Size Must be Supplied

The following code is illegal:
p = new char[];
A integral size must be supplied within the square brackets - either a literal or an integral variable. As a side note, the following is legal and standard:
p = new char[0];
The compiler heap allocation routine is required to return a unique address in heap memory, and that the deletion works correctly. This was put in the C++ standard specifically to allow generalized array handling - i.e. the special case of a size of 0 did not have to be explicitly checked.

Lifetime of Temporary Unnamed Objects

VC++ follows the traditional practice for the lifetime of temporary objects, which is typically to the end of the enclosing block (usually a function). The C++ standard defines the lifetime of a temporary object to be over at the end of the full expression in which is was created (typically the end of the statement where the temporary is being created). One way that this might unexpectedly manifest itself is as follows:
// class Acct needs a copy ctor, but developer left it out (error condition)
Acct a = Acct(b); // copy construct an Acct object a from an existing Acct object b
a.doSomething(); // high probability of crashing with a std C++ compiler
The reason this will have different behavior between an older non-standard compiler and a newer standard compiler is that a temporary unnamed object is being created in the copy construction. On a standard compiler the temporary is destroyed before the next statement, which in the above example may leave the newly constructed object in a corrupt state (most likely due to an internal pointer that is pointing to memory that has already been returned to the heap). On a non-standard conforming compiler, the logic error would not show up until the end of the function or block.

Standard Header Files and Namespaces

The C++ standard specifies all library header files without the .h extension. Everything in the standard C++ library is also wrapped in the std namespace. A few common header files have older, traditional versions, in particular <iostream.h>. For many applications and users the traditional, non-standard headers work fine. However, do not mix the non-standard version with the standard version, or there may be strange and problematic behavior (in particular if you try to mix <iostream> and <iostream.h>).

A general recommendation - if using one standard C++ library header file (such as <vector>), use all standard C++ header files.

Do not use the following for portable code - it is for Visual C++ compilers only (or at least for Windows development only):

#include <afx.h> // do NOT do this in portable code

There are many good sources on namespaces (including the textbook for the C++ Intro classes I teach), and the syntax and usage is not hard to learn. The most convenient way to use standard library classes and functions in the std namespace is with the using namespace std; directive. However, this is generally considered bad style since it places everything in the global namespace (which goes against one of the main reasons to have namespaces in the first place). Better style and usage is to simply put std:: in front of each item from the standard library, or to explicitly state each item being used from the namespace:

using std::cout;
using std::endl;
using std::vector;
In standard C++, the traditional C library headers are placed in both the traditional global and the std namespace and used with the following conventions:
#include <cstring>  // traditional C library string.h, in std namespace
#include <string.h> // traditional C library string functions, in global namespace
#include <cctype>   // traditional C library ctype.h, in std namespace
#include <ctype.h>  // traditional C library ctype functions, in global namespace
Note - both Visual C++ 5.0 and 6.0 do not correctly handle the C library headers wrapped in the std namespace, so for portable code the recommendation is to use the traditional .h versions of C library headers - e.g. #include <string.h> instead of #include <cstring>

Class Name as Scope Qualifier in Class Declaration

MS VC++ allows a class name scope qualifier as part of the class declaration:
class Test {
public:
  Test () : x(0) { }
  int Test::getX() const; // illegal C++, but not syntax error on VC++
private:
  int x;
};
int Test::getX() const {
  return x;
}
The commented line above is illegal C++, according to section 8.3/1 of the C++ standard. The correct syntax (and recommended style) is to only use class scope qualifiers outside of the class declaration.

Co-Variant Return Types on Virtual Functions

In standard C++, The return type of virtual functions overriding a base class virtual are allowed to vary, as long as the variance follows a standard conversion (through an inheritance relationship). As an example:
class UrlFactory { // Abstract Base Class
public:
  virtual UrlProxy* createInstance() = 0; // pure virtual
};

class HttpUrlFactory : public UrlFactory {
public:
  virtual HttpProxy* createInstance(); // HttpProxy is derived from UrlProxy
};

class FtpUrlFactory : public UrlFactory {
public:
  virtual FtpProxy* createInstance(); // FtpProxy is derived from UrlProxy
};
In all other respects virtual function signatures must exactly match.

VC++ does not implement co-variant return types, so workarounds must be applied. An example workaround would be as follows (which doesn't provide as much flexibility in the Factory class usage, but works for many usages):

class HttpUrlFactory : public UrlFactory {
public:
  virtual UrlProxy* createInstance(); // return base pointer instead of derived pointer
};

class FtpUrlFactory : public UrlFactory {
public:
  virtual UrlProxy* createInstance(); // return base pointer instead of derived pointer
};

Global 'using' Declarations (Metrowerks Bug, pre CW 5 update 3)

Prior to update 3 of Metrowerks CodeWarrior Pro 5, 'using' declarations at the global scope caused errors in certain circumstances:
#include <iostream>
#include <ctype.h>
using std::cout;
using std::endl;
int main()
// ...
  cout << "Something" << endl; // used to cause ambiguity errors

'std::pair' Type Promotion (Metrowerks Bug, pre CW 6)

Prior to CodeWarrior Pro 6, there is a bug with const type promotion of std::pair:
#include <string>
#include <map>
#include <utility>
int main() {
  std::multimap<std::string, std::string> testMap;
  std::string first("first"), second("second");
  testMap.insert(std::pair<const std::string, std::string>(first, second)); // compiles fine, as expected
  testMap.insert(std::make_pair(first, second)); // perfectly legal, but syntax error on CW 5
  return 0;
}

Types Stored in Standard Library Containers

The standard library containers (e.g. vector, list, deque) place some requirements on the types that the container object is instantiated with:
  • The class must have a default constructor (an unfortunate and irritating restriction).
  • If any kind of searching is required, the class must have operator== overloaded.
  • If any kind of ordering comparison is required, the class must have operator< overloaded.
  • A copy constructor and / or assignment operator will be called by the container class in many situations, and if needed the compiler will generate one of each. However, this is a common source of bugs, and many classes need to have their own copy constructors and assignment operators defined.

Command Line Arguments on Mac Computers, Metrowerks Compiler

A tip from Blake Herring, for Metrowerks compiler on a Mac:

There is a simple function in MCW that passes command line arguments to a program.  This is useful since you have no shell on a  Mac.

int main ( int argc, char * argv [ ] )  // compiles fine, but arguments not passed to app
MCW to the rescue:
#include <console.h>  // this is from Metrowerks C lib
int main ( int argc, char* argv[ ] ) ){
  argc = ccommand ( &argv );  // this line must be first thing after "main"
At runtime, the ccommand function in the <console.h> library will create a GUI interface that will give the option of passing arguments either from the keyboard or from a file.

This is documented in the Metrowerks help files, but it can be tough to come across if you don't know what to look for or don't know that it exists.

Note that for portable and standard code, the <console.h> and ccommand lines must have appropriate #ifdef guards.


This page constructed by Cliff Green, Copyright © 2001.