- 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
- Using the Swap() Algorithm
- Using class stopwatch for Performance Measurements
- Extending <tt><iostream></tt> to Support User-Defined Types
- Using <tt>auto_ptr</tt> To Automate Memory Management
- Using <tt>auto_ptr</tt> To Automate Memory Management, Part II
- Using <tt>auto_ptr</tt> To Automate Memory Management, Part III
- Using <tt>enum</tt>s as Mnemonic Indexes
- Create Objects on Pre-Allocated Memory Using <tt>Placement-new</tt>
- Online Books: <tt>Placement-new</tt>
- Bitwise Operators
- Bitwise Operators II
- Who's <tt>this</tt>?
- A Reference Guide
- The Virtues of Multiple Inheritance
- Interfaces
- Multiple Inheritance: Construction and Destruction Order
- nothrow new
- POD Initialization
- Object Initialization
- <tt>const</tt> Declarations
- The Semantics of <tt>volatile</tt>
- <tt>inline</tt> Functions
- Project Organization Guidelines
- All About <tt>bool</tt>
- <tt>typedef</tt> Declarations
- State of the <tt>union</tt>
- Dynamic Cast Uses
- Integrating C and C++
- <tt>const</tt>-Correctness
- <tt>const</tt>-Correctness: Advanced Issues
- Sprucing Up Legacy Code
- Virtual Constructors
- Naming Names
- Function Calls
- Speaking Standardese (updated)
- Speaking Standardese: the One Definition Rule
- Declarations and Definitions
- More on Declarations and Definitions
- The Most Vexing Parse
- Finally, At Last
- Sound Bytes (Admittedly Off Topic)
- Local Classes
- Complex Arithmetic
- Floating Point Woes
- String Manipulation
- The Object Model
- The Object Model II
- The Object Model III
- Temporary Objects
- Temporary Objects: Advanced Techniques
- Over-Engineering
- Security Enhancements
- Drop the (automatic) Pilot
- Choosing the Right Container
- Choosing the Right Container II
- Choosing the Right Container, Part III
- Arrays and Pointers
- Low-Level File I/O
- Low-Level File I/O Part II
- static Declarations, Part I
- static Declarations, Part II
- <code>static</code> Initialization Order
- Revisiting the Deprecation of File-Scope Static
- Virtual Memory and Memory Mapping
- Cellular Phone Programming Guidelines
- The Handle/Body Idiom
- Whole Program Optimization, Part I
- Whole Program Optimization, Part II
- Manipulating Directories
- Window Dressing
- <code>friend</code> Declarations
- <code>friend</code> Part II: the Interaction of Friendship and Template Classes
- Forcing Object Allocation on Specific Storage Types
- Lazy Evaluation
- Cache and Carry
- Controlling a Container’s Capacity
- Non-Blocking I/O, Part I
- Non-Blocking I/O, Part II
- Using Unions for Automatic Conversion
- Launching a Child Process
- <tt>switch</tt> Statements
- Introducing the "struct Hack"
- Scoped Enumerators
- Doing Statistics with STL
- Fixing the "Unresolved External" Linkage Error
- Understanding Calling Conventions
- Understanding the Empty Base Optimization
- Implementing RPC with the door Library, Part 1
- Implementing RPC with the door Library, Part 2
- Eliminating Two Common Pointer and <tt>sizeof</tt> Bugs
- Command Line Arguments
- Performance Myths Busting
- Tag Names And Types Part I
- Tag Names And Types Part II
- The Infamous goto
- Trimming Strings
- Can Objects Live Forever? Part I
- Can Objects Live Forever? Part II
- Five Ways to Improve Your Functions
- Member Aggregate Initialization
- Five Futile Coding-Style Debates
- The Good Parasite Idiom: An Exercise in OOD
- The Good Parasite Idiom: An Exercise in OOD, Part II
- The Good Parasite Idiom: An Exercise in OOD, Part III
- Ten Techniques to Reduce the Size of Your Classes, Part I
- Ten Techniques to Reduce the Size of Your Classes, Part II: Inheritance Issues
- Ten Techniques to Reduce the Size of Your Classes, Part III
- Ten Techniques to Reduce the Size of Your Classes IV
- Taking the Address of an Object with an Overloaded Operator <tt>&</tt>
- strcpy() -- How and Why Does It "Just Work"?
- Anonymous Structs
- Five Easy Ways to Reduce The Size of your Executables
- Standard Layout Classes and Trivially Copyable Types, Part I
- Standard Layout Classes and Trivially Copyable Types, Part II
- Five Simple Code Sanity Checks
- 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++
- C++0x Concurrency
- The Reflecting Circle
- We Have Mail
- The Soapbox
- Numeric Types and Arithmetic
- Careers
- Locales and Internationalization
The Most Vexing Parse
Last updated Jan 1, 2003.
C++ has parsing ambiguity between a function declaration and a constructor invocation when the constructor arguments are temporaries. This is known as the "most vexing parse" problem. C++09 doesn't resolve this ambiguity but there are several things you can do to avert it in the first place. Awareness is the first step.
The Problem
How would you interpret the last line of code in the following listing?
struct S {
S();
};
struct T {
T(const S& );
};
T v( S() ); //what's your interpretation?
Most programmers interpret the last line as a definition of an object v of type T which is initialized with a temporary S object. Indeed, several old compilers still interpret the code in this manner. However, the construct S() in a declaration context can also be an abstract declarator (a declarator without an identifier) meaning: a function with no parameters returning S by value. In that case, the construct S() is implicitly converted to the function pointer S(*)(). The entire statement T v( S() ) therefore can therefore mean a declaration of a function called v having a parameter of function-pointer type, and returning a value of type T.
In programming languages, there's rarely any true ambiguity. The parser has precedence rules that disambiguate what otherwise may seem as ambiguous statements. The statement
T v( S() ); //what's your interpretation?
is interpreted as a function declaration, not as a definition of an object because of a parsing precedence rule called "favor a declaration" which basically says: When a well-formed C++ statement can be interpreted as either a declaration or something else (a definition, an expression etc.), favor a declaration. You're already familiar with another parsing disambiguation rule called the maximal munch rule. We can thus conclude that
T v( S() );
Is a declaration of a function called v, not an object definition. Surprised? Confused? So are several C++ compilers. Now let's see how to resolve this issue.
Manual Disambiguation
By now you know that code containing the most vexing parse isn't really ambiguous. However, a human reader might find it ambiguous or even misinterpret it, assuming that the statement
T v( S() );
is a definition. The best approach is defensive programming -- avoid the seemingly ambiguous syntax in the first place. If you want to initialize an object, use an extra pair of parentheses:
T v( (S()) ); // v is an object initialized by a reference to a temporary
If you want to declare a function having a parameter of function-pointer type that returns a value of type T, use the following syntax instead:
T v( S(*)() ); // v is a function
As always, typedefs can simplify complex declarations.
C++09 and Quality of Implementation
You're probably wondering why the C++09 standard doesn't address the most vexing parse issue. The primary reason is that overriding the default interpretation (i.e., favoring an object initialization to a function declaration) might cause existing code to break. That said, using the new initialization syntax of C++09 you can easily make your intention clear:
T v={ S() };
Of course, traditional assignment syntax will also do the trick:
T v= S();
Although the latter is suspected of being less efficient because it might introduce unnecessary temporaries (in practice, compiler' should optimize away these temporaries).
Official C++ imposes no requirements on compilers with respect to the most vexing parse. However, several compilers issue diagnostics when they encounter such statements. The Apple-sponsored Clang compiler produces the following warning:
warning: parentheses were disambiguated as a function declarator T v( S() ); ^~~~~~~
Sun's Studio 12 C++ 5.9 compiler requires the +w option to issue a warning in this case but its warning is much clearer:
Warning: Declaration of "v" is a function, not an object.
Hopefully, a similar warning will become the default in the presence of the most vexing parse even if the C++ standard doesn't require this.
