410 likes | 622 Views
DEV 321 Understanding and Using Advanced C++ Template Features and Topics on ISO C++. Scott Currie Program Manager Visual C++ Microsoft Corporation. A word from the field about standards conformance. [After saying good things about VC++, which prompted chuckles from the audience]
E N D
DEV 321Understanding and Using Advanced C++ Template Features and Topics on ISO C++ Scott CurrieProgram Manager Visual C++ Microsoft Corporation
A word from the field about standards conformance [After saying good things about VC++, which prompted chuckles from the audience] "I may complain about Microsoft’s [C++] standards conformance, but it’s a good compiler. If you can get your code through the front end, the back end will generate amazingly efficient executables." -- Scott Meyers, Boston, March 2002
A word from the field about standards conformance [After saying good things about VC++, which prompted chuckles from the audience] "I may complain about Microsoft’s [C++] standards conformance, but it’s a good compiler. If you can get your code through the front end, the back end will generate amazingly efficient executables." -- Scott Meyers, Boston, March 2002
Agenda • Why you should care about conformance • A look at particular conformance areas: • Member templates(standard "iterator-range" constructors) • Partial template specialization(specializing std::vector; Loki::TypeList) • Koenig lookup (a.k.a. argument-dependent lookup, ADL) • Summary • Where to find out more • Q & A
Agenda • Why you should care about conformance • A look at particular conformance areas: • Member templates(standard "iterator-range" constructors) • Partial template specialization (specializing std::vector; Loki::TypeList) • Koenig lookup (a.k.a. argument-dependent lookup, ADL) • Summary • Where to find out more • Q & A
Why you should care about conformance • Library writers • Use it daily for PTS and other features • The rest of us • Can use some techniques directly • Library consumption has to work • Anyone who uses multiple compilers • Easier porting across compilers • Lower maintenance and fewer #ifdefs
Agenda • Why you should care about conformance • A look at particular conformance areas: • Member templates(standard "iterator-range" constructors) • Partial template specialization (specializing std::vector; Loki::TypeList) • Koenig lookup (a.k.a. argument-dependent lookup, ADL) • Summary • Where to find out more • Q & A
A look into the standard library • The standard library provides some useful containers: std::vector<int> v; // safer than C-style array std::list<SomeUserType> l; // much more flexible, too std::deque<bool> d; // pick a data structure… • You can construct a container in many ways… vector<int> v1; // empty by defaultvector<int> v2( 10 ); // 10 ints with value 0vector<int> v3( 10, 42 ); // 10 ints with value 42vector<int> v4( v1 ); // make a copy of v1 • …but not from another container? list<int> l; // a list of intsvector<int> v5( l ); // error – but isn’t this a// reasonable thing to do?
Constructing from an iterator range • To make this possible, the standard library containers can all be constructed also from an iterator range: list<int> l; // a list of intsvector<int> v5( l.begin(), l.end() ); // ok – aha! • Pointers are iterators too, so construct from an array… int a[] = { 3, 1, 4, 1, 5, 9, 2, 6, 5, 3, 5, 8, 9, 8 };vector<int> v6( a, a+14 );// or, strictly: &a[0], &a[ sizeof(a)/sizeof(int) ] • … or even from the console using istream_iterators: vector<string> v( istream_iterator<string>( cin ), (istream_iterator<string>()) );sort( v.begin(), v.end() );copy( v.begin(), v.end(), ostream_iterator<string> out( cout, " " ) );
Constructor templates • There are infinitely many possible iterator types, so construction from an iterator range must be a member template to work with them all: namespace std { template <class T, class Allocator = allocator<T> > class vector { template <class InputIterator> vector( InputIterator first, InputIterator last /*...*/ ); // ... much more stuff ... };}
VC++ and member templates • Of course, there’s more to member template functions than just constructors • Templated assignment operators • Templated accessor functions • Templated conversions • etc. • Member templates now all work correctly in VC++ 2003 • These examples all worked in VC++ 2002 too • VC++ 6.0 could not reliably handle any of this
Limitations of member templates • Keep an eye open for a tie-back slide in the Partial Template Specialization section • A member function template shall not be virtual (14.5.2, p3) • Virtual dispatch does not allow compiler to do the right thing statically • So, the compiler emits a table of pointers for virtual members of each derived type (VTable) • This would cause VTable explosion with a single compiland • Impossible with multiple compilands unless a smart linker got involved
Agenda • Why you should care about conformance • A look at particular conformance areas: • Member templates (standard "iterator-range" constructors) • Partial template specialization (specializing std::vector; Loki::TypeList) • Koenig lookup (a.k.a. argument-dependent lookup, ADL) • Summary • Where to find out more • Q & A
A look at std::vector • Here’s that fundamental tool again: namespace std { template <class T, class Allocator = allocator<T> > class vector { /* ... lots of stuff ... */ };} • You can specialize vector for your own types: class MyType { /* ... */ }; namespace std {template<>// explicit specialization class vector<MyType>// for T == MyType { /* … lots of stuff … */ };} • Note: The above is a complete specialization (we know exactly what all the template arguments are)
Why partial template specialization • But what if your type is itself also a template? template<class T> class MyType { /* ... */ }; namespace std {template<>// explicit specialization??? class vector<???>// for T == ??? { /* … lots of stuff … */ };} • We couldn’t possibly write out all the complete specializations (even if we wanted to) • Instead, we want a “partial” specialization – one that is still parameterized, but for a subset of types: namespace std {template<class T>// partial specialization class vector<MyType<T> >// for MyType<T> { /* … lots of stuff … */ };}
Digression: Boost and Loki • Boost • Many excellent template libraries that work well with standard library • Started by members of C++ Standards Committee Library Working Group • Free, open source, peer reviewed • http://www.boost.org/ • Loki • “A C++ library of designs, containing flexible implementations of common design patterns and idioms” • Read Modern C++ Design • http://sourceforge.net/projects/loki-lib/
Digression: auto_ptr • Be careful with std::auto_ptr • Do not point auto_ptr at an array • It only deletes. No delete[] • Do not put an auto_ptr in a vector • Can’t be indexed in a standard way • Remember, auto_ptr uses single ownership • Use Boost smart pointers instead • shared_ptr • Performs reference counting • Using a shared_ptr to hold the results of every new will nearly eliminate the possibility of memory leaks
A look at Loki::Typelist • Here’s a fundamental tool from Loki, a list of types: template <class T, class U>struct Typelist { typedef T Head; typedef U Tail;}; • Sample usage (simplified by macros ): TYPELIST_1(int)// Typelist<int, NullType> TYPELIST_2(int, double)// Typelist<int, Typelist<double, NullType> > // etc.
Why partial template specialization • Calculate the length of a typelist: template <class TList> struct Length; template <> struct Length<NullType> { enum { value = 0 };}; template <class T, class U> // partial specializ.struct Length< Typelist<T, U> > { enum { value = 1 + Length<U>::value };}; • Sample usage: Length< TYPELIST_3(int, double, bool) >::value // 3 • (Remember, this is all being done at compile time!)
Why partial template specialization • Select the Nth type of a typelist: template <class TList, unsigned index> struct TypeAt; template <class Head, class Tail> // partial specializ.struct TypeAt<Typelist<Head, Tail>, 0> { typedef Head Result;}; template <class Head, class Tail, unsigned int i> // againstruct TypeAt<Typelist<Head, Tail>, i> { typedef typename TypeAt<Tail, i - 1>::Result Result;}; • Sample usage: TypeAt< TYPELIST_3(int, double, bool), 1>::Result// double
Why partial template specialization • Other operations on typelists: • Search:IndexOf<> • Append:Append<> • Erase:Erase<> • Remove duplicates:NoDuplicates<> • Replace:Replace<> • Partial sort according to an inheritance hierarchy (really!):DerivedToFront<> • Every single one of these requires partial specialization (or "partial workarounds") because they’re defined recursively with a special case (or two) to end the recursion
Generics + OO = a boatload of power • Something more obviously(?) useful (& with TTPs!): template <class TList, template <class> class Unit>class GenScatterHierarchy; template <class T1, class T2, template<class> class Unit>class GenScatterHierarchy<Typelist<T1, T2>, Unit> : public GenScatterHierarchy<T1, Unit> , public GenScatterHierarchy<T2, Unit> { /*…*/ }; template <class AtomicType, template <class> class Unit>class GenScatterHierarchy : public Unit<AtomicType> { /*…*/ }; template <template <class> class Unit>class GenScatterHierarchy<NullType, Unit> { /*…*/ }; • This lets a class inherit from every type in a flexible list of types.
Member Templates • Here is the tie-back • Cannot be partially specialized • Only explicit specialization • How do we achieve similar functionality? • Overloading • Provides very similar pattern matching • Code bloat
A word about compiler-busting • Remember, this is all "executed" at compile time! • To quote Herb Sutter, about Modern C++ Design: "… ‘can compiler XYZ compile Loki?’ is becoming something of an unofficial benchmark in compiler-writer circles. … Loki is written entirely in normal standard-conforming C++ — only more standard and more conforming, it turns out, than some compilers are yet quite ready to digest. Loki … uses templates so widely and heavily that it tears the tar and stresses the stuffing out of some current compilers. In fact, at the time the book was released [2001], no commercially available compiler could compile all of Loki correctly…"
PTS Summary • Bread and butter of advanced library writer • Provide basis of compile-time programming • Two types of PTS • Template template parameter • Specify subset of multiple template parameters • Not available for member templates • They work in Visual C++ .NET 2003 • You should use to specialize library types for your own types and templated types • Read Modern C++ Design!
Agenda • Why you should care about conformance • A look at particular conformance areas: • Member templates (standard "iterator-range" constructors) • Partial template specialization (specializing std::vector; Loki::TypeList) • Koenig lookup (a.k.a. argument-dependent lookup, ADL) • Summary • Where to find out more • Q & A
A motivating example • And now for a word from our standard… namespace NS{ class T { }; void f(T);} NS::T parm; int main() {f(parm); // no function f() seems to be in} // scope -- is all lost?
A motivating example • And now for a word from our standard… namespace NS{ class T { }; void f(T);} NS::T parm; int main() {f(parm); // OK: calls NS::f()} • The parameter comes from namespace NS, therefore in addition to looking for candidate functions/functors in all the usual places, the compiler is required to also look in NS
How important is it really? • Q: Is this purely a notational convenience? What’s so hard about writing "NS::f(parm);" or "using NS::f;"? #include <iostream>#include <string> // gives std::operator<<() for strings int main() { std::string hello = "Hello, world"; std::operator<<( std::cout, hello ); // ugh // "std::cout << hello;" is what we really want} • It would be disgraceful if the programmer had to qualify this name, because either the operator couldn’t be used naturally, or the user would have to write "using std::operator<<;" (tedious) or "using namespace std;" (although I recommend the latter for other reasons…)
Actually, you have it in VC++ .NET • But VC++ (.NET) 2002 did Koenig lookup only for operators • Happens to be a very common case • Happens to speak directly to the foregoing example • VC++ 2003 does Koenig lookup for all functions, as required by the standard • Useful example of what VC++ 2003 buys you?
Reimplementing std::swap • Basics namespace NS { swap(tuple x, tuple y) {…} tuple<int,int> x; tuple<int,int> y; … swap(x,y); // OK: calls NS::swap() } • What if swap is out of namespace? namespace NS { swap(tuple x, tuple y) {…} } tuple<int,int> x; tuple<int,int> y; …. swap(x,y); // BAD: calls std::swap() NS::swap(x,y); // OK: calls NS::swap(), but annoying
More std::swap • What if now we want a generic method? namespace NS { swap(tuple x, tuple y) {…} } tuple<int,int> x; tuple<int,int> y; template <class T> void swap_helper(T source, T target) { …. swap(source, target); // Need Koenig lookup …. } swap_helper <tuple <int,int> > (x,y);
Koenig Summary • Argument Dependent Lookup • Be aware of operator example • Understand what Koenig Lookup is all about • Getting around the lack of namespace passing ability to generic code • If you use namespaces, be careful about name collisions and the ordering behavior • If you use namespaces and generics, you will likely have to depend on this
Agenda • Why you should care about conformance • A look at particular conformance areas: • Member templates (standard "iterator-range" constructors) • Partial template specialization (specializing std::vector; Loki::TypeList) • Koenig lookup (a.k.a. argument-dependent lookup, ADL) • Summary • Where to find out more • Q & A
Best practices (on any good compiler) • Know your community and the community libraries: • full STL (see also Effective STL) • Standard gets revised, so stay up to date • Boost (see www.boost.org) • Loki (see www.moderncppdesign.com) • etc. • Know your power tools: • partial specialization (see also Modern C++ Design) • Koenig lookup (see also Exceptional C++) • member template functions
VC++ and you(r projects) • You now have one of the most standards-conforming implementations of one of the most powerful and flexible programming languages in the world • Use Standard C++ to your best advantage: • "Explore, try, perform" • Don’t be afraid of making full use of advanced language features • Don’t be afraid of exploring and exploiting the full power of community libraries like Boost and Loki and MTL
Further Reading • A. Alexandrescu. Modern C++ Design(Addison-Wesley, 2001) • The official documentation for the Loki library. See also www.mcppdesign.com and my review of this book at: • "Review of Modern C++ Design"(C/C++ Users Journal, 20(4), April 2002),www.gotw.ca/publications/mcd_review.htm • N. Josuttis. The C++ Standard Library(Addison-Wesley, 1999) • S. Meyers. Effective STL (Addison-Wesley, 2002) • H. Sutter. Exceptional C++ andMore Exceptional C++ (Addison-Wesley, 2000 & 2002) • H. Sutter. Guru of the Week (www.gotw.ca)
Community Resources • MS Community Sites • http://msdn.microsoft.com/visualc • http://gotdotnet.com/team/cplusplus • List of newsgroups • http://msdn.microsoft.com/newsgroups(Visual Tools and Languages -> C/C++) • Attend a free chat or webcast • http://microsoft.com/communities/chats/default.mspx • http://microsoft.com/usa/webcasts/default.asp • C++ User Groups and Forums • http://www.mvps.org/vcfaq • http://cplus.about.com/cs/visualc • http://www.accu.org • http://www.codeguru.com • http://www.codeproject.com • http://www.devx.com/cplus/Door/7042
Agenda • Why you should care about conformance • A look at particular conformance areas: • Member templates (standard "iterator-range" constructors) • Partial template specialization (specializing std::vector; Loki::TypeList) • Koenig lookup (a.k.a. argument-dependent lookup, ADL) • Summary • Where to find out more • Q & A
© 2003 Microsoft Corporation. All rights reserved. This presentation is for informational purposes only. MICROSOFT MAKES NO WARRANTIES, EXPRESS OR IMPLIED, IN THIS SUMMARY.