- Overview
-
Table of Contents
- Special Member Functions: Constructors, Destructors, and the Assignment Operator
- Operator Overloading
- Memory Management
- Templates
- Namespaces
- Time and Date Library
- Streams
- Object-Oriented Programming and Design Principles
- The Standard Template Library (STL) and Generic Programming
- Exception Handling
- Runtime Type Information (RTTI)
- Signal Processing
- Creating Persistent Objects
- Bit Fields
- New Cast Operators
- Environment Variables
- Variadic Functions
- Pointers to Functions
- Function Objects
- Pointers to Members
- Lock Files
- Design Patterns
- Dynamic Linking
- Tips and Techniques
- Five Things You Need to Know About C++11 Unions
- A Tour of C99
- A Tour of C1X
-
C++0X: The New Face of Standard C++
- Reference Wrapper
- The Performance Technical Report
- auto for the People
- Ironing Templates' Syntactic Wrinkles
- Visual C++ Becomes ISO Compliant
- A Garbage Collector for C++
- C99 Core Features in C++0X
- The <code>shared_ptr</code> Class
- The shared_ptr Class, II
- Lambda Expressions and Closures, Part I
- Lambda Expressions and Closures, Part II
- Lambda Expressions and Closures, Part III
- The Type Traits Library, Part I
- The Type Traits Library, Part II
- The Type Traits Library, Part III
- finally Revisited
- The Any Library
- The nullptr Keyword Proposal
- Delegating Constructors
- The Explicit Conversion Operators Proposal
- Conditionally-Supported Behavior
- The weak ptr Class Template, Part I
- The weak ptr Class Template, Part II
- POD Types Revisited
- The rvalue Reference Proposal, Part I
- The rvalue Reference Proposal, Part II
- Proposal for New String Algorithms
- Concepts, Part I
- Concepts, Part II
- constexpr: Generalized Constant Expressions
- The <u>constexpr</u> Proposal: Constructors
- Strongly-Typed enum Types
- C++09: The Road Ahead
- C++09: Proposals by Statuses
- Changing Undefined Behavior to Diagnosable Errors
- New Character Types
- The __func__ Predeclared Identifier is Coming to C++
- Static Assertions
- The extern template Proposal
- Variadic Templates, Part I
- Variadic Templates, Part II
- Variadic Templates, Part III -- Critique
- Using unique_ptr, Part I
- Using unique_ptr, Part II
- Unrestricted Unions, Part I
- Unrestricted Unions, Part II
- Unrestricted Unions, Part III
- Types With No Linkage as Template Arguments
- New Initialization Syntax
- Initializer Lists and Sequence Constructors
- New Standard Library Algorithms
- Class Member Initializers
- Inheriting Constructors
- Introducing Attributes
- The Removal of Concepts From C++0x
- The Future of C++0x, Part I
- The Future of C++0X, Part II
- The Debate About Attributes, Part I
- The Debate About Attributes, Part II
- The Debate About Attributes, Part III
- The Debate About Attributes, Part IV
- Forward Declarations of Enum Types
- The SCARY Iterators Proposal, Part I
- The SCARY Iterators Proposal, Part II
- Heading for Deprecation: <tt>export</tt>, Exception Specification and <tt>register</tt>
- The Rejection of the Unified Function Syntax Proposal
- Rvalue References as Object Members
- FCD Approved
- The Debate on noexcept, Part I
- The Debate on noexcept, Part II
- The Debate on noexcept, Part III
- About-face -- [[Attributes]] to Be Replaced with Keywords
- Will Delegating Constructors Be Removed From C++0x?
- Rvalue References: Past, Present and Future, Part I
- Rvalue References: Past, Present and Future, Part II
- Rvalue References: Past, Present and Future, Part III
- A Move in the Right Direction, Part I
- A Move in the Right Direction, Part II
- New Keywords for Inheritance Control, Part I
- New Keywords for Inheritance Control, Part II
- FDIS Approved
- C++0x Concurrency
- The Reflecting Circle
- We Have Mail
- The Soapbox
- Numeric Types and Arithmetic
- Careers
- Locales and Internationalization
constexpr: Generalized Constant Expressions
Last updated Jan 1, 2003.
The C++98 rules regarding constant expressions are too restrictive. They force implementers to use low-tech macros instead of inline functions to ensure the compile-time evaluation of constant expressions. This issue as well as other limitations of C++98 constant expression rules have led to formation of a generalized constant expression mechanism for C++09.
The Problem with Functions In Constant Expressions
The std::numeric_limits class template is meant to replace C’s <limits.h>. For example, the expression
numeric_limits<int>::max()
is functionally equivalent to the macro INT_MAX. max() is in fact an inline static function that returns a constant value:
static inline int numeric_limits<int>::max () { return INT_MAX; }
However, there is a crucial difference between the following two expressions:
const int x=INT_MAX; const int y=numeric_limits<int>::max() ;
x is an integral constant whereas the numeric_limits<int>::max() function call isn’t. Although both statements will compile, y cannot appear in a context that requires a constant expression (although it’s declared const). Consider:
int arr[x]; //fine int arr2[y]; //error, "constant expression required"
Seemingly, there should be no difference between x and y in this respect since numeric_limits::max() is inlined. The compiler is therefore able to "see through" the max() function call, treating the initializations of both x and y in the same manner. However, due to an unnecessarily restrictive notion of constant expressions in C++98, a function call, when used as an initializer, is considered a dynamic initializer, regardless of whether the call is inlined. This urges users to prefer macros when values need to be known at compile time.
Surprising Dynamic Initialization
A similar problem is witnessed with the initialization of const static data members. Consider:
struct V {
static const int g;
};
const int a = 10 * V::g; // dynamic initialization
const int V::g = 98; //initialization comes too late
Seemingly, a can be computed at compile time because the value V::g is known at compile time. However, the initialization of V::g appears too late to allow the compile time evaluation of a. Consequently, a is initialized dynamically. If you place the initializer of V::g before the definition of a, a becomes a constant expression:
const int V::g = 98; //compile time initialization const int a = 10 * V::g; //compile time initialization
There are at least two problems with this state of affairs:
- There’s no syntactic clue to suggest that a’s initialization is deferred to runtime.
- Because of its dynamic initialization, a cannot be used in a context that requires a constant expression.
The generalized constant expressions proposal<> by Reis, Stroustrup and Maurer addresses these problems.
Constant Expression Functions
The authors propose a new construct called constant expression functions. A constant expression function is a function that can be "executed" at compile time by means of inlining and evaluating the result at compile-time. A function is a constant-expression function if it meets the following criteria:
- It returns a value (i.e., it’s not a void function)
- The body of the function consists of a single return statement of the form:
return expr;
- The function is declared as constexpr
The expression expr in the return statement must evaluate to a constant expression after substitution. Additionally, a constant expression function shall not be called before it has been defined. Here are a few examples:
constexpr int f(int x)
{ return x * x; } // OK
constexpr long long_max()
{ return 2147483647; } // OK
constexpr int abs(int n)
{ return x < 0 ? -n : n; } // OK
constexpr void g(int n) // error: void function
{ return; }
constexpr int next(int x)
{ return ++x; } // error: ++ not allowed in a constant expression
constexpr int g(int x) // error: body contains more than return expr;
{int n = x;
while (--x > 1) n *= x;
return n;
}
constexpr int quadruple(int y);
enum { size = quadruple(16) }; // error: quadruple() isn’t defined yet
template<typename T>
constexpr int getsizeof(T t)
{ return sizeof (t); } // OK
The comment "OK" indicates that the function body can be evaluated as a constant expression given constant expression argument(s).
Constant-expression data
The constexpr specifier may apply to variables and data members. A variable or data member declared with the constexpr specifier is called a constant-expression value. It must be initialized with a constant expression. For example:
const double m = 10; constexpr double e = m * quadruple(9.6); // OK extern const int s; constexpr int a = quadruple(s); // error: quadruple(s) is not a constant expression
A variable or data member declared with constexpr behaves as if it were declared const. However, unlike a const variable or data member, it requires initialization before it’s used and its initializer must be a constant-expression. These requirements ensure that a constexpr variable can always be used as a constant expression, e.g., in an array definition.
