Alex Rivera | Logout

Are C++ Templates just Macros in disguise?

Asked 2008-10-07T20:43:07.177
72

I've been programming in C++ for a few years, and I've used STL quite a bit and have created my own template classes a few times to see how it's done.

Now I'm trying to integrate templates deeper into my OO design, and a nagging thought keeps coming back to me: They're just a macros, really... You could implement (rather UGLY) auto_ptrs using #defines, if you really wanted to.

This way of thinking about templates helps me understand how my code will actually work, but I feel that I must be missing the point somehow. Macros are meant evil incarnate, yet "template metaprogramming" is all the rage.

So, what ARE the real distinctions? and how can templates avoid the dangers that #define leads you into, like

  • Inscrutable compiler errors in places where you don't expect them?
  • Code bloat?
  • Difficulty in tracing code?
  • Setting Debugger Breakpoints?
Edit
Report

2 Answers

2

In my opinion, macros are a bad habit from C. Although they can be useful for some I do not see a real need for them when there are typedefs and templates. Templates are the natural continuation to Object Oriented Programming. You can do a lot more with templates...

Consider this...

int main()
{
    SimpleList<short> lstA;
    //...
    SimpleList<int> lstB = lstA; //would normally give an error after trying to compile
}

In order to make the conversion you can use something that is called a conversion constructor and a sequence constructor (look at the end) along the rather complete example for a list:

#include <algorithm>

template<class T>
class SimpleList
{
public:
    typedef T value_type;
    typedef std::size_t size_type;

private:
    struct Knot
    {
        value_type val_;
        Knot * next_;
        Knot(const value_type &val)
        :val_(val), next_(0)
        {}
    };
    Knot * head_;
    size_type nelems_;

public:
    //Default constructor
    SimpleList() throw()
    :head_(0), nelems_(0)
    {}
    bool empty() const throw()
    { return size() == 0; }
    size_type size() const throw()
    { return nelems_; }

private:
    Knot * last() throw() //could be done better
    {
        if(empty()) return 0;
        Knot *p = head_;
        while (p->next_)
            p = p->next_;
        return p;
    }

public:
    void push_back(const value_type & val)
    {
        Knot *p = last();
        if(!p)
            head_ = new Knot(val);
        else
            p->next_ = new Knot(val);
        ++nelems_;
    }
    void clear() throw()
    {
        while(head_)
        {
            Knot *p = head_->next_;
            delete head_;
            head_ = p;
        }
        nelems_ = 0;
    }
    //Destructor:
    ~SimpleList() throw()
    { clear(); }
    //Iterators:
    class iterator
    {
        Knot * cur_;
    public:
        iterator(
answered 2009-09-08T19:11:32.863
2

Templates understand data types. Macros do not.

This means that you can do stuff like the following...

  • Define an operation (e.g., one for wrapping numbers) that can take any data type, then provide specializations that pick the appropriate algorithm based on whether the data type is integral or floating point
  • Determine aspects of your data types at compile time, permitting tricks like template deduction of array size, which Microsoft uses for its C++ overloads of strcpy_s and its ilk

Additionally, because templates are type safe, there are a number of template coding techniques that could conceivably be performed with some hypothetical advanced preprocessor but would be kludgy and error-prone at best (e.g., template template parameters, default template arguments, policy templates as discussed in Modern C++ Design).

answered 2009-12-14T18:33:51.523

Your Answer