6.5.2. Inline and constexpr Functions

In § 6.3.2 (p. 224) we wrote a small function that returned a reference to the shorter of its two string parameters. The benefits of defining a function for such a small operation include the following:

• It is easier to read and understand a call to shorterString than it would be to read and understand the equivalent conditional expression.

• Using a function ensures uniform behavior. Each test is guaranteed to be done the same way.

• If we need to change the computation, it is easier to change the function than to find and change every occurrence of the equivalent expression.

• The function can be reused rather than rewritten for other applications.

There is, however, one potential drawback to making shorterString a function: Calling a function is apt to be slower than evaluating the equivalent expression. On most machines, a function call does a lot of work: Registers are saved before the call and restored after the return; arguments may be copied; and the program branches to a new location.

inline Functions Avoid Function Call Overhead

A function specified as inline (usually) is expanded “in line” at each call. If shorterString were defined as inline, then this call

cout << shorterString(s1, s2) << endl;

(probably) would be expanded during compilation into something like

cout << (s1.size() < s2.size() ? s1 : s2) << endl;

The run-time overhead of making shorterString a function is thus removed.

We can define shorterString as an inline function by putting the keyword inline before the function’s return type:

// inline version: find the shorter of two strings
inline const string &
shorterString(const string &s1, const string &s2)
{
        return s1.size() <= s2.size() ? s1 : s2;
}


Image Note

The inline specification is only a request to the compiler. The compiler may choose to ignore this request.


In general, the inline mechanism is meant to optimize small, straight-line functions that are called frequently. Many compilers will not inline a recursive function. A 75-line function will almost surely not be expanded inline.

constexpr Functions
Image

A constexpr function is a function that can be used in a constant expression (§ 2.4.4, p. 65). A constexpr function is defined like any other function but must meet certain restrictions: The return type and the type of each parameter in a must be a literal type (§ 2.4.4, p. 66), and the function body must contain exactly one return statement:

constexpr int new_sz() { return 42; }
constexpr int foo = new_sz();  // ok: foo is a constant expression

Here we defined new_sz as a constexpr that takes no arguments. The compiler can verify—at compile time—that a call to new_sz returns a constant expression, so we can use new_sz to initialize our constexpr variable, foo.

When it can do so, the compiler will replace a call to a constexpr function with its resulting value. In order to be able to expand the function immediately, constexpr functions are implicitly inline.

A constexpr function body may contain other statements so long as those statements generate no actions at run time. For example, a constexpr function may contain null statements, type aliases (§ 2.5.1, p. 67), and using declarations.

A constexpr function is permitted to return a value that is not a constant:

// scale(arg) is a constant expression if arg is a constant expression
constexpr size_t scale(size_t cnt) { return new_sz() * cnt; }

The scale function will return a constant expression if its argument is a constant expression but not otherwise:

int arr[scale(2)]; // ok: scale(2) is a constant expression
int i = 2;         // i is not a constant expression
int a2[scale(i)];  // error: scale(i) is not a constant expression

When we pass a constant expression—such as the literal 2—then the return is a constant expression. In this case, the compiler will replace the call to scale with the resulting value.

If we call scale with an expression that is not a constant expression—such as on the int object i—then the return is not a constant expression. If we use scale in a context that requires a constant expression, the compiler checks that the result is a constant expression. If it is not, the compiler will produce an error message.


Image Note

A constexpr function is not required to return a constant expression.


Put inline and constexpr Functions in Header Files

Unlike other functions, inline and constexpr functions may be defined multiple times in the program. After all, the compiler needs the definition, not just the declaration, in order to expand the code. However, all of the definitions of a given inline or constexpr must match exactly. As a result, inline and constexpr functions normally are defined in headers.


Exercises Section 6.5.2

Exercise 6.43: Which one of the following declarations and definitions would you put in a header? In a source file? Explain why.

(a) inline bool eq(const BigInt&, const BigInt&) {...}

(b) void putValues(int *arr, int size);

Exercise 6.44: Rewrite the isShorter function from § 6.2.2 (p. 211) to be inline.

Exercise 6.45: Review the programs you’ve written for the earlier exercises and decide whether they should be defined as inline. If so, do so. If not, explain why they should not be inline.

Exercise 6.46: Would it be possible to define isShorter as a constexpr? If so, do so. If not, explain why not.


..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
3.15.182.159