- 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
shared_ptrClass - 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 constexpr 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: export, Exception Specification and register
- 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
Ironing Templates' Syntactic Wrinkles
Last updated Feb 25, 2005.
The template syntactic rules were carved in stone in 1997, when the technical work on the final draft of the C++ standard was completed. However, in the past eight years, we have gained more experience and savviness with templates -- as have our compilers.
This real world experience has encouraged programmers and compiler writers to propose core language extensions that would make template programming a tad more intuitive by removing a few rigorous restrictions or by allowing compilers to take charge of things. Here I present some of these proposals.
The Angle Bracket Blues
C++ programmers who aren't new to templates have learned the hard way why their compiler moans if it sees a declaration such as this:
vector<vector<char>> lines; //compilation error because of >>
My faithful (albeit a bit behind) C++ BuilderX issues this cryptic error message:
"main.cpp": E2451 Undefined symbol 'lines' at line 4".
This isn't exactly an elucidating error message, but I learned what my compiler is really trying to say: the sequence >> can't be parsed as two template delimiters. Because of the "maximal munch" rule, it's parsed as the right shift operator, which of course makes no sense at all in this declaration. Comeau gives a much clearer error message:
line 4: error: space required between adjacent ">" delimiters of
nested template argument lists (">>" is the right shift operator)
It's nice that compilers tell you exactly what's wrong. However, if a compiler already knows what the programmer meant, why not "do the right thing" and treat the sequence >> as two closing angle brackets? Such a proposal has been made several times. In fact, there are compilers that already silently treat the >> sequence as two closing angle brackets in this context, often issuing a warning message.
As tempting as it is, this proposal raises two problems. First, it violates the "maximal munch" rule. Although this rule isn't sanctified, "special cases" are best avoided in general. There is a however a more serious problem. In certain (rare) occasions, the >> sequence might be truly ambiguous:
template <int N> class Cont;
template <class T> void funky() {}
template <int I> void funky() {}
int main()
{
funky<Cont<8>>4> > (); // << here is the right shift op
}
However, such contrived examples are pretty rare. Besides, the compiler has a few hints: In the first example, the >> token appears after a type-name. As such, it cannot be a valid right shift operator anyway.
The typename Keyword
The typename keyword disambiguates qualified names. By default, in a qualified name of the form
C::mem; //mem is treated as a non-type identifier
mem is assumed to be an object or a variable of class C. When programmers wish to override this interpretation, they precede the typename keyword, indicating that mem is a type, not an object. So far, so good. Yet there are contexts in which typename is not allowed, to the surprise of most programmers. In the following example, all of the occurrences of typename are valid, except for the last one:
template <typename T> class Vec{ //ok
public:
typedef T ELEM;
};
template <typename T> //ok
void func( typename Vec<T>::ELEM & e) //ok
template <>
void func( typename Vec<T>::ELEM & e) //error
This restriction seems too rigorous. The last typename is indeed redundant: the compiler must see this specialization after seeing the primary func() template, so it already knows that Vec<T>::ELEM must be a type-name. However, compilers can simply ignore the redundant typename in this case and accept the code as is. Ignoring typename wouldn't be a precedent. You'd be amazed of how many keywords compilers nowadays ignore auto, register, and, in many cases, inline are the first candidates. volatile, restrict<> and long (when used in a declaration of long double variables, in environments that don't support this type) are other examples. Adding another keyword to this list shouldn't cause any harm.
Using Quoted Literals as Template Arguments
It's illegal to use a quoted literal as a template argument:
template <const char * txt>
class Logger
{
//..
void Write();
};
Logger<"Test"> lg().Write(); //error
The same restriction applies to floating point literals. Prohibiting quoted literals isn't an arbitrary whim as it might first appear. The problem is this: the "Test" literal is of type const char [5]. However, when it's used as a template argument its type decays to const char *. Can you see what the problem is? If we were to create multiple occurrences of the Logger template with the same quoted literal, there is no guarantee that these occurrences would refer to the same instance because each quoted literal might have a different memory address:
int main()
{
Logger<"Test"> lg().Write();
}
void func()
{
Logger<"Test"> lg().Write(); //a separate instance or not?
}
Still, programmers find this restriction too rigorous. With a few compiler switches, it is possible to avert the instance multiplication problem. Furthermore, some compilers that accept this code as a nonstandard extension store the literals in the internal representation of the template.
Summary
It's too early to tell which of these proposed extensions will make it into C++0x, if any. However, past experience shows that when a critical mass of compilers enables a certain feature as a nonstandard extension, that feature is eventually incorporated into ISO C++. We've witnessed this phenomenon with hashed containers, regular expressions and C99' inline.



