What is the difference between a macro and a const in C++?

Macros and constants are not remotely the same thing, each is sometimes appropriate for the circumstances, and your answer only scratches at the surface of the difference. Also, C++ has two different kinds of constants.

A constant defined with the const qualifier is best thought of as an unmodifiable variable. It has all the properties of a variable: it has a type, it has a size, it has linkage, you can take its address. (The compiler might optimize away some of these properties if it can get away with it: for instance, constants whose address is never used may not get emitted into the executable image. But this is only by the grace of the as-if rule.) The only thing you can’t do to a const datum is change its value. A constant defined with enum is a little different. It has a type and a size, but it doesn’t have linkage, you can’t take its address, and its type is unique. Both of these are processed during translation phase 7, so they can’t be anything but an lvalue or rvalue. (I’m sorry about the jargon in the preceding sentence, but I would have to write several paragraphs otherwise.)

A macro has far fewer constraints: it can expand to any sequence of tokens, as long as the overall program remains a well-formed program. It doesn’t have any of the properties of a variable. Applying sizeof or & to a macro may or may not do something useful, depending on what the macro expands to. Macros are sometimes defined to expand to numeric literals, and such macros are sometimes thought of as constants, but they’re not: “the compiler proper” (that is, translation phase 7) sees them as numeric literals.

It is generally considered good practice, nowadays, not to use a macro when a constant will do. Macros don’t obey the same scoping rules as all other identifiers, which can be confusing, and if you use a constant you give more information to translation phase 7 and thus also to the debugger. However, macros permit you to do things that cannot be done any other way, and if you need to do one of those things, you should not hesitate to use them. (Macros that are pulling their weight, in this sense, generally do not just expand to numeric literals, though I am not going to say never.)

EDIT: Here’s an example of a macro doing something interesting. It is in no way, shape or form a constant. There may well be a way to get the same effect without a macro (if you know one that doesn’t involve stringstreams, I’d be curious to hear about it!) but I think it makes a good illustration of both the power and the danger of macros (for the latter, consider what it would do if it was used outside of one very specific context…)

static double elapsed()
{ ... }
#define ELAPSED '[' << std::fixed << std::setprecision(2) << elapsed() << "] "

// usage:
for (vector<string>::iterator f = files.begin(); f != files.end(); f++) {
    cout << ELAPSED << "reading file: " << *f << '\n';
    process_file(*f);
}

Leave a Comment