Presented by Jonathan Caves
Visual C++ Business Unit
Microsoft Corporation
This paper lists the C++ language features that are supported for the first time in Microsoft® Visual C++® 5.0, or that have undergone a major revision since Microsoft Visual C++ 4.0. Where applicable, we will describe problems that users may encounter while using these new features.
For a full description of these C++ language features please see the latest ANSI/ISO working paper for the draft proposed standard for C++, or the online documentation contiguous supplied with Visual C++ 5.0
Visual C++ 5.0 includes support for the C++ built-in type “bool” along with the Boolean values “true” and “false”. This type can be used as follows:
bool flag = true; bool test(int value1, int value2) { flag = false; return value + value2 > 0 }
The only special rule about the built-in type “bool” is that when a postfix or prefix version of “operator ++” is applied to a variable of type “bool”, the value of the variable is set to “true”. The postfix and prefix versions of “operator-” cannot be applied to a variable of type “bool”. In arithmetic operations a Boolean value of “true” is converted to “1” while a Boolean value of “false” is converted to “0”.
void f() { int i = 2; i += true; DASSERT(i == 3); }
Visual C++ 5.0 defines a macro __BOOL_DEFINED that can be used to wrap code that is dependent on whether or not “bool” is supported.
#if !defined(__BOOL_DEFINED) typedef int bool #define true 1 #define false 0 #endif
Support for “bool” can be turned off by using the “/noBool” compiler switch.
A common problem we discovered when we converted to using “bool” instead of “int” was that a lot of programs did not respect the fact that a Boolean variable (even when it is represented by an “int”) should only take one of two values. For example, code like the following was not uncommon.
void f(BOOL fFlag) { switch (fFlag) { case TRUE: //Ö break; case FALSE: //Ö break; case 3: //Ö break; } }
And
BOOL Test(); void f() { if (Test() == 3) { //Ö } }
Both of the above code fragments will generate warnings when compiled with Visual C++ 5.0.
Another problem that was discovered while testing the use of “bool” internally was that there was existing iostream code that had the C++ operator precedence wrong. This “bug” did not manifest itself as long as “int” or some other built-in type was used to represent “bool”. However, once “bool” was fully supported as a separate type the code broke. The code in question looks something like the following (it is shown in a much-reduced pre-bool form).
typedef int bool; #define true 1 #define false 0 class ostream { public: // Ö ostream &operator<<(int); }; class X { public: // Ö bool Test() const { return ‘some-expr’ ? true : false; } }; ostream& operator<<(ostream& os, const X& x) { os << x.Test() ? '1' : '0'; return os; }
The problem is with the statement.
os << x.Test() ? '1' : '0';
Due to the fact that operator << has a higher precedence than operator ?: this statement is parsed by the C++ grammar as follows.
(os << x.Test()) ? '1' : '0';
when what the user intends is the following:
os << (x.Test() ? '1' : '0');
When “bool” is fully supported as a built-in type the code breaks because class ostream does not have a member “operator<< (bool)”.
Visual C++ 5.0 includes support for the C++ storage class specifier “mutable”. This specifier can only be applied to non-const, non-static, data member of a class. Any data member, which is declared to be mutable, can be modified from within a “const” member function; for example, with Visual C++ 5.0 the following code fragment will compile without error.
class X { public: bool GetFlag() const { m_accessCount++; return m_flag; } private: bool m_flag; mutable int m_accessCount; };
This will compile without error as “m_accessCount” has been declared to be “mutable”, and therefore can be modified from within “GetFlag ()” even though “GetFlag ()” is a const member function.
Visual C++ 5.0 includes support for the C++ function specifier “explicit”. This specifier can only be applied to the declarations of constructors within class declarations.
class X { public: explicit X(int); // legal explicit X(double) { // legal // Ö } }; explicit X::X(int) { // illegal // Ö }
An “explicit” constructor cannot take part in implicit conversions-it can only be used to explicitly construct an object, e.g., using the above class.
void f(X) { // Ö } void g(int i) { f(i); // Will cause an error } void h() { X x1(1); // Legal }
class X { public: explicit X(int); // Meaningful explicit X(double, int = 0); // Meaningful explicit X(int, int, int); // Superfluous };
While previous versions of Visual C++ did support local classes they did not support local class member functions, Visual C++ 5.0 supports this feature. For example, the following code fragment will compile without error.
void f() { class X { public: void mf() { } }; X x; x.mf(); }
Note The following restrictions apply to local classes:
The proposed ANSI/ISO standard for C++ requires that compilers parse and syntax check template definitions. This feature is implemented in Visual C++ 5.0. This means that Visual C++ 5.0 can detect many errors that previous versions of Visual C++ could not detect. For example, the following code will give an error with Visual C++ 5.0 but not with Visual C++ 4.0.
template<class T> class X { public: void mf() const; }; template<class T> void X<T>::mf() { // Error: Missing ‘const’ // Ö }
This also means that the compiler will detect syntax errors in template classes that are defined, but never instantiated. With previous versions of Visual C++, if a class or function template were never instantiated, then the compiler would not check its definition for errors.
While testing this feature internally we came across three common errors that users may run into once they start using Visual C++ 5.0.
The first problem is the case of a type being used in a template definition before it has been declared, but then the type is declared before the first instantiation, or use, of the template. This would compile with Visual C++ 4.0, but it will not compile with Visual C++ 5.0 because, as Visual C++ 5.0 syntax checks the class template declaration, it requires that the type has been declared.
template<class T> class X { // Ö Data m_data; // Error seen here with Visual C++ 5.0 }; class Data { // Ö }; void g() { X<int> x1; }
The fix for this problem is to move the definition of the class Data to before the definition of the class template X.
The second problem is a member function being defined outside of a class template when it was never declared inside the class.
template<class T> class X { // No mf declared here }; // This definition did not cause an error with Visual C++ 4.0 but // it will cause an error with Visual C++ 5.0 // template<class T> void X<T>::mf() { // Ö }
The third problem is related to the first problem and has to do with the fact that a class is considered to be a “normal” class unless it has been explicitly declared to be a class template. The following code will produce errors with Visual C++ 5.0 but not with Visual C++ 4.0.
template<class T> class X { friend class Y<T>; // Parsed as Y 'less-than' T // 'greater-than' Z<T> *mf(); // Parsed as Z 'less-than' T // 'greater-than' }; template<class T> class Y { }; template<class T> class Z { }; X<int> x;
To correct this problem, a user should forward declare Y and Z before the definition of X.
template<class T> class Y; template<class T> class Z; template<class T> class X { // Ö };
Visual C++ 5.0 supports the reuse of template parameters within template parameter lists. For example, code like the following is now supported.
class Y { // Ö }; template<class T, T* pT> class A { // Ö }; Y aY; A<Y, &aY> a;
In Visual C++ 5.0 a template parameter can also be used as the default argument for a subsequent template parameter. For example, in the following code fragment the class template specialization B<int> is identical to the class template specialization B<int, int>:
template<class T1, class T2 = T1> class B { // Ö }; B<int> b1; B<int, int> b2;
Visual C++ 5.0 supports the new keyword “typename”. This keyword has two uses.
The first use is that “typename” can be used instead of “class” in template parameter lists. For example, the following two template parameter lists are identical.
template<class T1, class T2> // Ö template<typename T1, typename T2> // Ö
The second use of “typename” is within class template definitions where “typename” is used to indicate, during the syntax checking of the template definition, that a qualified name that is dependent on a template parameter denotes a type. This is necessary as under strict ANSI/ISO C++ rules the following code should produce an error, as any name used within a template definition is assumed not to name a type unless the lookup of the name finds a type. As the compiler knows nothing about “T” (apart from the fact that it is some generic type), it cannot know anything about “N”; thus, it should not treat “N” as a type.
template<class T> class X { typedef T::N M; };
The introduction of the keyword “typename” allows the user to explicitly specify to the compiler that N is a type. For example, the following code should compile without an error.
template<class T> class X { typedef typename T::N M; };
Note As “typename” only applies to the immediately following qualified name, the two “statements” cannot be separated. For example, the following code fragment will generate an error.
template<class T> class X { typename T::N; typedef T::N M; // Illegal - ‘N’ is not a type name };
It is illegal to instantiate a class template with a type which does not declare the identifier introduced via a “typename” as a type. For example, the following two instantiations of the class template X will generate errors.
class A { // No ‘N’ declared }; class B { public: int N; // A ‘N’ is declared but is not a type }; X<A> x1; X<B> x2;
Note In Visual C++ 5.0 “typename” is only really necessary if the user compiles with /Za (ANSI C++ mode). Under /Ze (Extended C++ mode) the compiler assumes that any name in a qualified name that it cannot resolve via normal name lookup is a type-if a type can appear in the current context in the C++ grammar.
template<class T> class X { T::N x; // Compiler assumes 'N' is a type void mf() { T::mf(); // Compiler assumes 'mf' is an identifier } };
In previous versions of Visual C++ each of the template arguments associated with a function template had to be deduced from the function arguments at the call site. Visual C++ 5.0 allows users to explicitly specify the template arguments. For example, the following code is now legal.
template<typename T> void f(T) { // Ö } void g(char j) { f<int>(j); // generate the specialization ‘f(int)’ }
When a template argument is explicitly specified in this way, implicit conversions are allowed in order to convert the function argument to the type of the corresponding function parameters. For example, in this case the compiler will convert “j” to type “int”. Implicit conversions are not allowed when a template argument is deduced.
The addition of this feature provides a way for calling function templates that have a generic return type. This was impossible in previous versions of Visual C++.
template<typename T1, typename T2> T1 convert(T2 t2) { // Ö } int g(char c) { return convert<int>(c); }
Visual C++ 5.0 supports the new syntax for declaring explicit specializations of function or class templates. For example, the following code is now accepted by Visual C++ 5.0.
template<typename T> class X { }; template<typename T> void f(T t) { } // Explicit specialization of X with ‘int’ // template<> class X<int> { }; // Explicit specialization of f with ‘char’ with the // template argument explicitly specified // template<> void f<char>(char c) { } // Explicit specialization of f with ‘double’ with the // template argument deduced // template<> void f(double d) { }
In order to support existing code, Visual C++ 5.0 continues to allow the older syntax.
// Explicit specialization of X with ‘char’ // class X<char> { }; // Explicit specialization of f with ‘char; with the // template argument deduced. // void f(char) { }
Visual C++ 5.0 supports the explicit instantiation of function templates-previous versions only supported the explicit instantiation of class templates. Thus, the following code is now accepted.
template<typename T> void f(T) { } // Instantiate f with the explicitly specified // template argument 'int' // template void f<int>(int); // Instantiate f with the deduced template // argument 'char' // template void f(char);
Visual C++ 5.0 allows users to import or export instantiations of a template class or function from a DLL. This is achieved by explicitly instantiating the template in question with the desired arguments and marking the instantiation as either dllimport or dllexport.
template<typename T> class X { }; // Explicitly instantiate X with ‘int’ and export it from the DLL // template class __declspec(dllexport) X<int>;
When a class template is explicitly instantiated, a definition must be available for each of the members of the class. The following code fragment will generate diagnostics when compiled with Visual C++ 5.0.
template<typename T> class X { public: void mf(); private: static int m_flag; }; // No definition for either the member function ‘mf’ or // for the static data member ‘m_flag’ // template class __declspec(dllexport) X<char>;
Note A template definition itself cannot be marked for export or import. For example, the following is illegal.
template<typename T> class __declspec(dllexport) X { };
In Visual C++ 4.0 the namespace using-directive was implemented incorrectly. For example, given the following code Visual C++ 4.0 would print out “data = 4” instead of the correct answer “data = 0”.
namespace N { int data = 4; } void f(bool flag) { int data = 0; if (flag) { using namespace N; printf("data = %d\n", data); } } void main() { f(true); }
This problem has been fixed in Visual C++ 5.0.
Just before Visual C++ 4.0 shipped, the ANSI/ISO C++ committee changed the way qualified-names were looked up in the presence of namespace using-directives. The committee decided that the following code should be legal.
namespace A { int flag = 0; } namespace B { using namespace A; } namespace C { using namespace A; using namespace B; } void main() { printf("C::flag = %d\n", C::flag); }
The qualified-name “C::flag” should resolve to “A::flag” due to the namespace using directives within namespace C. Visual C++ 5.0 implements this feature.
Visual C++ 5.0 supports the definition of member operators for array new and array delete. For example, the following code will compile.
class X { public: void* operator new[](size_t); void operator delete[](void*); }; void f() { X *pX = new X[5]; delete [] pX; }
Visual C++ 5.0 includes an implementation of the C++ Standard Library.
The C++ Standard Library grew out of the C++ standardization process. It is a specification of a collection of classes and routines for extending the C++ language. An implementation of the C++ Standard Library first shipped with Visual C++ 4.2.
Language support
Language support provides access to limits; memory allocation; new and delete; exception processing; definition of exception; and typeinfo.
Types |
<cstddef> |
Implementation Properties |
<limits>
<cfloat> |
Start and Termination |
<cstdlib> |
Dynamic Memory Management |
<new> |
Type Identification |
<typeinfo> |
Exception Handling |
<exception> |
Runtime Support |
<cstdarg>
<cstdlib> |
Diagnostics
Diagnostics defines the standard exceptions that can be generated by the C++ Standard Library, such as, logic_error, domain_error, invalid_argument.
Exception Classes |
<stdexcept> |
Assertions |
<cassert> |
Error Numbers |
<cerrno> |
General utilities
General utilities defines utilities that are required by the rest of the library, such as allocators, comparison operators, and functional objects.
Utilities |
<utility> |
Function Objects |
<functional> |
Memory |
<memory> |
Date and Time |
<ctime> |
Strings
Strings provide support for character manipulation, where a character may be a “char”, or a “wchar_t”, or some other user specified representation.
String Classes |
<string> |
C String Utilities |
<cctype>
<cstdlib> |
Localization
Localiazation provides support for the localization of software, such as how a date should be represented and how a monetary value should be printed.
Locales |
<locale> |
C Locales |
<clocale> |
Containers
Containers provide C++ access to common data structures, such as list, stack.
Sequences |
<bitset>
<vector> |
Associative Containers |
<map> <set> |
Iterators
Iterators are common iterators, such as input, output, forward, and reverse.
Iterators |
<iterator> |
Algorithms
Common algorithms, such as find, sort, replace, and merge.
Algorithms |
<algorithm> |
C Algorithms |
<cstdlib> |
Numerics
Numerics provide C++ support for complex and valarray.
Complex Numbers |
<complex> |
Numeric Arrays |
<valarray> |
General Numeric Operations |
<numeric> |
C Numeric Operations |
<cmath> <cstdlib> |
Input/Output Library
Input/output Libraries provide C++ I/O support using streams, such as iostream, and fstream.
Forward Declarations |
<iosfwd> |
Iostream objects |
<iostream> |
Iostream base classes |
<ios> |
Stream Buffers |
<streambuf> |
Formatting and Manipulators |
<istream>
<iomanip> |
String Streams |
<sstream> <cstdlib> |
File Streams |
<fstream>
<cwchar> |
Since Visual C++ 4.2, the following changes have been made to the Visual C++ implementation of the C++ Standard Library.
Support for the C++ Standard Library in a DLL
Visual C++ 5.0 comes with a version of the C++ Standard Library in a DLL (msvcprt.lib and msvcp50.dll). Therefore, users no longer have to always statically link to the C++ Standard Library.
Support for “std” namespace
In accordance with the draft ANSI/ISO C++ Standard, the C++ Standard Library is now defined within the “std” namespace. Users must therefore change their existing code in order to be able to access elements of the C++ Standard Library.
#include <list> // Use a ‘using directive’ // using namespace std list<int> g_list; // or a ‘using declaration’ // using std::list; list<int> g_list; // or explicitly qualify the name // std::list<int> g_list;
Support for default allocators
As Visual C++ 5.0 supports default template parameters, users no longer have to explicitly specify an allocator and, in some cases, a container whenever they specialize a container from the C++ Standard Library.
#include <stack> // Visual C++ 4.2 // stack<int, deque<int, allocator<int> > > g_stack; // Visual C++ 5.0 // std::stack<int> g_stack;
Due to the way that Visual C++ 5.0 handles the member functions of a class template that are fully defined within the class, users may find that they are unable to specialize a C++ Standard Library container with a user-defined type. For example, the following code fragment will not compile cleanly.
#include <stack> using namespace std; class X { // Ö private: int m_data; }; stack<X> g_stack; // Errors here
The reason this code will not compile is that when Visual C++ 5.0 instantiates a specialization of a class template it also instantiates all the member functions that are fully defined within the class, regardless of whether they are used or not. This means, in the case of the stack container, that Visual C++ 5.0 instantiates both of the following operators.
bool std::stack<int>::operator<(const std::stack<int> &) const; bool std::stack<int>::operator==(const std::stack<int> &) const;
The instantiation of these functions causes (eventually) the compiler to search for the following two declarations.
bool X::operator<(const X &) const; bool X::operator==(const X &) const;
As the user has not declared these functions the compiler will generate appropriate error messages.
The solution to this problem is for the user to add the above declarations to his or her class. For example, X should be defined as follows.
class X { // Ö bool operator<(const X &) const; bool operator==(const X &) const; private: int m_data; };
Note Only the declarations of these functions are required. As the functions that require these declarations are not called, they will be removed at the link phase, and thus linker errors will be avoided.
If the user really does want to compare two containers, then the member comparison operators need to be defined; otherwise, linker error will occur.
In some cases it may also be necessary to declare a default constructor. For example, if class X had been defined as follows:
class X { oublic: X(int); // Ö bool operator<(const X &) const; bool operator==(const X &) const; private: int m_data; };
Then a default constructor would be necessary, as many of the container routines (like the constructor for deque) are defined along the following lines.
template<class _Ty, class _A = allocator<_Ty> > class deque { public: explicit deque(size_type _N, const _Ty& _V = _Ty(), const _A& _Al = _A()); // ... };
Again the problem occurs because that Visual C++ 5.0 is doing more processing than is really necessary. In this case it is processing the default argument even though it is not required.
© 1997 Microsoft Corporation. All rights reserved.
The information contained in this document represents the current view of Microsoft Corporation on the issues discussed as of the date of publication. Because Microsoft must respond to changing market conditions, it should not be interpreted to be a commitment on the part of Microsoft, and Microsoft cannot guarantee the accuracy of any information presented after the date of publication.
This document is for informational purposes only. MICROSOFT MAKES NO WARRANTIES, EXPRESS OR IMPLIED, IN THIS SUMMARY.
Microsoft and Visual C++ are registered trademarks of Microsoft Corporation.
Other product or company names mentioned herein may be the trademarks of their respective owners.