Java supplies wrappers for all the built-in types. As it turns out, C++ can profit from similar wrappers.
Copyright © 1998 by Robert Allan Schwartz
C++ guarantees the initialization of all objects upon creation. This holds true regardless of the storage class (auto, local static, global static, heap). As long as you implement your constructors properly (i.e., you initialize all the data members), then you will never again have to track down a bug due to an uninitialized object.
Unfortunately, the same can't be said for built-in types. Variables of built-in types are initialized to zero by default for the local static and global static storage classes, but auto and heap variables of built-in type are not initialized at all. Thus, you may still have to track down a bug due to an uninitialized variable of built-in type.
Because built-in types are not classes, they do not have constructors. How can you guarantee the initialization of all variables of built-in type upon creation, regardless of the storage class or lack of constructors?
Each built-in type can be wrapped in a wrapper class. The wrapper class has a constructor, and that constructor initializes the built-in type data member. To do the same thing for all built-in types, you can write a template class.
template <class T> class builtin { public: builtin(T new_value = 0) {value = new_value;} // ... private: T value; };See Figure 1 for an example of how to use the template class.
Making the Wrapper Transparent
Wrapping the built-in type in a wrapper class causes the type of the data member to be altered. Can this alteration be hidden, so all users of b in Figure 1 still think they are working with an int? Figure 2 shows an implementation of this technique.
- The default constructor uses a default parameter value of zero, which is convertible to all built-in types.
- Because the compiler-generated copy constructor, destructor, and operator= are sufficient, you do not need to define your own. However, I have given them here for completeness.
- The conversion operator operator T is invoked implicitly whenever a T is expected, but I provide a builtin<T>.
- Anyone taking the address of something that apparently has type T wants a T *, not a builtin<T> *, hence the need for the overloaded versions of operator &.
- The state-changing operators (i.e., assignment, compound assignment, increment, decrement) are required.
- The overloaded operators that take non-const references to builtin<T> (i.e. extractor) are required.
- The overloaded operators that take const references to builtin<T> (i.e. inserter, list given below) are not necessary because of the conversion operator operator T:
builtin<int> b1, b2, b3; b1 = b2 + b3; // becomes: b1 = // (operator int(b2)) + // (operator int(b3));This means that the following operators do not need to be defined, but I have included them here for completeness:
unary: operator+ operator- operator~ operator! binary: operator>> (bit-shift) operator<< (bit-shift) operator<< (inserter) operator== operator!= operator< operator<= operator> operator>= operator+ operator- operator* operator/ operator% operator& operator^ operator|The ANSI standard doesn't specify where a template class's member function definitions should reside, so different compilers tend to look in different places. For portability, all the functions are inline, making a .cpp file unnecessary. Using inline functions also means no run-time cost for using the wrapper class.
A benefit provided by builtin<T> is that more classes will able to use a compiler-generated constructor, so programmers will be able to write less code (see Figure 3).
Another benefit provided by builtin<T> is that it is possible to derive from builtin<T>, although it is not possible to derive from built-in types.
Conclusion
It could be argued that builtin<T> would be completely unnecessary if everyone always initialized their variables of built-in type. It could also be argued that if everyone always wrote correct software, then there would be no need for debuggers.
Initialization of built-in type variables and data members can always be guaranteed by wrapping the built-in type in a wrapper class, even in classes that rely on their compiler-generated constructor. Doing so changes the type of the data member, but that can be hidden with suitable overloaded operators. The wrapper class is a template class, so it will suffice for all the built-in types. The use of inline functions means there is no run-time cost for using the wrapper class.
Acknowledgements
A draft of this paper was posted to comp.lang.c++. Readers Siemel Naran and Jim Hyslop kindly provided valuable feedback and suggestions.
Robert Allan Schwartz teaches the course "Design Patterns in C++" for Tessellation Training. He can be reached at ras@tessellation.com.