Even if function-like macros may look similar to functions, they work differently. For example, functions provide parameter type-checking, whereas
macros do not. Furthermore, since macros result in textual replacements, the code within a macro argument may be evaluated multiple times or in
unexpected ways.
Generally, functions provide a more secure and reliable mechanism than function-like macros. This safety usually outweighs the speed advantages
allegedly offered by macros. Therefore, whenever possible, functions should be preferred.
Noncompliant code example
#define CUBE (X) ((X) * (X) * (X)) // Noncompliant
void func(void) {
int i = 2;
int a = CUBE(++i); // Noncompliant. Expands to: int a = ((++i) * (++i) * (++i))
// ...
}
Compliant solution
inline int cube(int i) {
return i * i * i;
}
void func(void) {
int i = 2;
int a = cube(++i); // yields 27
// ...
}
Exceptions
In a few situations, actual functions can’t replace function-like macros because the macro relies on features that only work with textual
replacement. For instance:
- Using manipulation of tokens, such as
##
(token-pasting) and #
(stringification).
- Getting information about the context into which the macro is expanded by using
__FILE__
, __LINE__
,
__func__
, or other similar compiler-specific constructs. Note that C++20 std::source_location
can be a good replacement
for some of these use cases — see S6190.
This rule will ignore macros that make use of those features.