410 likes | 542 Views
Standard Libraries. B. Stroustrup Nov 2010. Overview. smart pointers; see shared_ptr , weak_ptr , and unique_ptr garbage collection ABI Improvements to algorithms array Improvements to containers forward_list a singly-liked list tuple and other utilities thread Time utilities
E N D
Standard Libraries B. Stroustrup Nov 2010
Overview • smart pointers; see shared_ptr, weak_ptr, and unique_ptr • garbage collection ABI • Improvements to algorithms • array • Improvements to containers • forward_list a singly-liked list • tuple and other utilities • thread • Time utilities • abandoning a process • future and promise • atomic operations • random number generators • unordered_map • metaprogramming and type traits • regex a regular expression library • scoped allocators
Overview • Containers • array a fixed-sized array/vector • Improvements to containers • forward_list a singly-liked list • How does a C++0x container differ from a C++98 container • Utilities • tuple • function • Algorithms
Tuples • In the old days • Just use N arguments where N is more than you need struct unused {}; template<class T1 = unused, class T2 = unused, class T3 = unused> class Tuple { // can be used with 0 to N arguments // How big does N has to be in real life? T1 v1; T2 v2; T3 v3; // a waste of space public: Tuple(); Tuple(T1); Tuple(T1,T2); Tuple(T1, T2, T3); // a lot of typing // … };
Tuple • Avoid the unused arguments and wasted space // specialize (and don’t bother implementing the general case): template<> class Tuple<> { public: Tuple(); // … }; template<class T1> class Tuple<T1> { T1 v1; public: Tuple(T1); // … }; // …
Tuple • What do you want tuples for? • Heterogeneous structures • that you don’t want to write “by hand” • pair<T1,T2> is the simplest tuple • (and arguably most useful) • E.g. name/value pair • E.g. return-code/value pair • List of bound arguments in a bound function (Currying) • E.g. the implementation of std::bind()
Tuples • Note • We never store the types (only values of types) • There is no standard run-time representation of types in C++ • Like all template-based schemes tuples are compiler-based • The tuple values are stored contiguously • Not as a linked structures • The zero-overhead principle holds
Tuples • How to make a general (variadic) tuple • Rumor has it that you can do it differently “but not easily or elegantly” template<typename... Elements> class tuple; // general tuple (never used) template<typename Head, typename... Tail> // N-tuple for 1<=N class tuple<Head, Tail...> : private tuple<Tail...> // note: base class depending on template argument // note: private base class { Head head; // each added type adds one member to the final class public: // … }; template<> class tuple<> { // zero-tuple // … };
Tuples • How to make a general (variadic) tuple template<typename Head, typename... Tail> // N-tuple for 1<=N class tuple<Head, Tail...> : private tuple<Tail...> // note: base class depending on template argument // note: private base class { Head head; // each added type adds one member to the final class public: tuple(); tuple(const tuple&); template <class... UTypes> tuple(const tuple<UTypes...>&); tuple& operator=(const tuple&); template <class... UTypes> tuple& operator=(const tuple<UTypes...>&); void swap(tuple& rhs); };
Tuples • How to make a general (variadic) tuple template<VariableType... Types> // remove implementation details // for standards description class tuple { public: // add concepts //add rvalue constructors and assignments //add allocators to constructors and assignments };
20.5.2 Class template tuple template <VariableType... Types> class tuple { public: requires DefaultConstructible<Types>... tuple(); template <class... UTypes> requires Constructible<Types, UTypes&&>... explicit tuple(UTypes&&...); requires CopyConstructible<Types>... tuple(const tuple&); template <class... UTypes> requires Constructible<Types, const UTypes&>... tuple(const tuple<UTypes...>&); template <class... UTypes> requires Constructible<Types, RvalueOf<UTypes>::type>... tuple(tuple<UTypes...>&&); template <class... UTypes> requires Constructible<Types, const UTypes&>... template <class... UTypes> requires Constructible<Types, RvalueOf<UTypes>::type>... //allocator-extended constructors template <Allocator Alloc> requires ConstructibleWithAllocator<Types, Alloc>... tuple(allocator_arg_t, const Alloc& a); template <Allocator Alloc, class... UTypes> requires ConstructibleWithAllocator<Types, Alloc, UTypes&&>... explicit tuple(allocator_arg_t, const Alloc& a, UTypes&&...); template <Allocator Alloc, class... UTypes> requires ConstructibleWithAllocator<Types, Alloc, const UTypes&>... tuple(allocator_arg_t, const Alloc& a, const tuple<UTypes...>&); template <Allocator Alloc, class... UTypes> requires ConstructibleWithAllocator<Types, Alloc, RvalueOf<UTypes>::type>... tuple(allocator_arg_t, const Alloc& a, tuple<UTypes...>&&); template <Allocator Alloc, class... UTypes> requires ConstructibleWithAllocator<Types, Alloc, const UTypes&>... tuple(allocator_arg_t, const Alloc& a, const pair<UTypes...>&); template <Allocator Alloc, class... UTypes> requires ConstructibleWithAllocator<Types, Alloc, RvalueOf<UTypes>::type>... tuple(allocator_arg_t, const Alloc& a, pair<UTypes...>&&); requires CopyAssignable<Types>... tuple& operator=(const tuple&); template <class... UTypes> requires HasAssign<Types, const UTypes&>... tuple& operator=(const tuple<UTypes...>&); template <class... UTypes> requires HasAssign<Types, RvalueOf<UTypes>::type>... tuple& operator=(tuple<UTypes...>&&); template <class... UTypes> requires HasAssign<Types, const UTypes&>... template <class... UTypes> requires HasAssign<Types, RvalueOf<UTypes>::type>... requires Swappable<Types>... void swap(tuple&& rhs); };
20.5.2 Class template tuple template <VariableType... Types> class tuple { public: requires DefaultConstructible<Types>... tuple(); // default constructor requires CopyConstructible<Types>... tuple(const tuple&); // copy constructor: same type: T u2 = u; template <class... UTypes> requires Constructible<Types, UTypes&&>... explicit tuple(UTypes&&...); // move from arguments: T u(a,b,c) template <class... UTypes> requires Constructible<Types, const UTypes&>... tuple(const tuple<UTypes...>&); // copy from compatible type: T u = v; template <class... UTypes> requires Constructible<Types, RvalueOf<UTypes>::type>... tuple(tuple<UTypes...>&&); // move from compatible type: T u = v; template <class... UTypes> requires Constructible<Types, const UTypes&>... tuple(const pair<UTypes...>& u); // copy first u.first and u.second template <class... UTypes> requires Constructible<Types, RvalueOf<UTypes>::type>... tuple(pair<UTypes...>&& u); // move from first u.first and u.second };
20.5.2 Class template tuple template <VariableType... Types> class tuple { // … // allocator-extended constructors // each of these constructors passes its constructor on to its members template <Allocator Alloc> requires ConstructibleWithAllocator<Types, Alloc>... tuple(allocator_arg_t, const Alloc& a); template <Allocator Alloc, class... UTypes> requires ConstructibleWithAllocator<Types, Alloc, UTypes&&>... explicit tuple(allocator_arg_t, const Alloc& a, UTypes&&...); template <Allocator Alloc, class... UTypes> requires ConstructibleWithAllocator<Types, Alloc, const UTypes&>... tuple(allocator_arg_t, const Alloc& a, const tuple<UTypes...>&); template <Allocator Alloc, class... UTypes> requires ConstructibleWithAllocator<Types, Alloc, RvalueOf<UTypes>::type>... tuple(allocator_arg_t, const Alloc& a, tuple<UTypes...>&&); template <Allocator Alloc, class... UTypes> requires ConstructibleWithAllocator<Types, Alloc, const UTypes&>... tuple(allocator_arg_t, const Alloc& a, const pair<UTypes...>&); template <Allocator Alloc, class... UTypes> requires ConstructibleWithAllocator<Types, Alloc, RvalueOf<UTypes>::type>... tuple(allocator_arg_t, const Alloc& a, pair<UTypes...>&&); // … };
20.5.2 Class template tuple template <VariableType... Types> class tuple { // … requires CopyAssignable<Types>... // assignments tuple& operator=(const tuple&); template <class... UTypes> requires HasAssign<Types, const UTypes&>... tuple& operator=(const tuple<UTypes...>&); template <class... UTypes> requires HasAssign<Types, RvalueOf<UTypes>::type>... tuple& operator=(tuple<UTypes...>&&); template <class... UTypes> requires HasAssign<Types, const UTypes&>... tuple& operator=(const pair<UTypes...>&); template <class... UTypes> requires HasAssign<Types, RvalueOf<UTypes>::type>... tuple& operator=(pair<UTypes...>&&); requires Swappable<Types>... void swap(tuple&& rhs); // and of course we can swap tuples };
Tuple helper functions • Make a tuple (deduce the argument types): template<MoveConstructible... Types>tuple<VTypes...> make_tuple(Types&&... t); • Access Ith element template <size_t I, VariableType... Types> requires True<(I < sizeof...(Types))> typenametuple_element<I, tuple<Types...> >::type& get(tuple<Types...>& t); • Tuples that are pairs of iterators are (in places) common template<InputIteratorIter> concept_map Range<tuple<Iter, Iter> > { // [Iter[0];Iter[1]) typedefIteriterator; Iter begin(tuple<Iter, Iter>& p) { return std::get<0>(p); } Iter end(tuple<Iter, Iter>& p) { return std::get<1>(p); } } • ==, !=, <, <=, >, >=
Tuple and pair • A 2-tuple is not a pair, but • You can construct a tuple from a pair • You can assign a pair to a tuple • You can access a pair like a tuple (get<1>() and get<2>()) • You cannot construct a pair from a tuple • You cannot assign a tuple to a pair
Tuple references • Standard 20.5 • Variadic template paper • Boost::tuple
20.7 Function objects • Lots of simple stuff (e.g., less) • Lots of (more or less) deprecated stuff (e.g., mem_fun) • Binders (20.7.12) • Think Currying template<CopyConstructible Fn, CopyConstructible... Types> unspecified bind(Fn, Types...); // C++0x template<Returnable R, CopyConstructible Fn, CopyConstructible... Types> unspecified bind(Fn, Types...); // legacy • Function wrappers (20.7.16) • Think pointers to functions and callbacks template<FunctionType> class function; //undefined general template template<Returnable R, CopyConstructible... ArgTypes> class function<R(ArgTypes...)>;
20.7.16.2 function template<Returnable R, CopyConstructible... ArgTypes> class function<R(ArgTypes...)> : public unary_function<T1, R> // iffsizeof...(ArgTypes) == 1 and // ArgTypes contains T1 : public binary_function<T1, T2, R> // iffsizeof...(ArgTypes) == 2 and // ArgTypescontains T1 and T2 { public: typedef R result_type; // 20.7.16.2.1, construct/copy/destroy: explicit function(); function(nullptr_t); function(const function&); function(function&&); template<class F> requires CopyConstructible<F> && Callable<F, ArgTypes...> && Convertible<Callable<F, ArgTypes...>::result_type, R> function(F); template<class F> requires CopyConstructible<F> && Callable<F, ArgTypes...> && Convertible<Callable<F, ArgTypes...>::result_type, R> function(F&&); template<Allocator Alloc> function(allocator_arg_t, const Alloc&); template<Allocator Alloc> function(allocator_arg_t, const Alloc&, nullptr_t); template<Allocator Alloc> function(allocator_arg_t, const Alloc&, const function&); template<Allocator Alloc> function(allocator_arg_t, const Alloc&, function&&); template<class F, Allocator Alloc> function(allocator_arg_t, const Alloc&, F); template<class F, Allocator Alloc> function(allocator_arg_t, const Alloc&, F&&); function& operator=(const function&); function& operator=(function&&); function& operator=(nullptr_t); template<class F> requires CopyConstructible<F> && Callable<F, ArgTypes..> && Convertible<Callable<F, ArgTypes...>::result_type function& operator=(F); template<class F> requires CopyConstructible<F> && Callable<F, ArgTypes...> && Convertible<Callable<F, ArgTypes...>::result_type, R> function& operator=(F&&); template<class F> requires Callable<F, ArgTypes...> && Convertible<Callable<F, ArgTypes...>::result_type, R> function& operator=(reference_wrapper<F>); ~function(); // 20.7.16.2.2, function modifiers: void swap(function&); template<class F, Allocator Alloc> requires Callable<F, ArgTypes...> && Convertible<Callable<F, ArgTypes...>::result_type, R> void assign(F, const Alloc&); // 20.7.16.2.3, function capacity: explicit operator bool() const; // deleted overloads close possible hole in the type system template<class R2, class... ArgTypes2> bool operator==(const function<R2(ArgTypes2...)>&) = delete; template<class R2, class... ArgTypes2> bool operator!=(const function<R2(ArgTypes2...)>&) = delete; // 20.7.16.2.4, function invocation: R operator()(ArgTypes...) const; // 20.7.16.2.5, function target access: const std::type_info& target_type() const; template <typename T> requires Callable<T, ArgTypes...> && Convertible<Callable<T, ArgTypes...>::result_type, R> T* target(); template <typename T> requires Callable<T, ArgTypes...> && Convertible<Callable<T, ArgTypes...>::result_type, R> const T* target() const; }; template <class R, class... Args> concept_mapUsesAllocator<function<R(Args...)>, Alloc> { typedefAllocallocator_type; }
20.7.16.2 function • Actually, simple to use function<float (int x, int y)> f; // make a function object structint_div { // take something you can call using () float operator()(int x, int y) const { return ((float)x)/y; }; }; f = int_div(); // assign cout << f(5, 3) << endl; // call through the function object accumulate(b,e,1,f); // passes beautifully
Function and member functions • Member functions can be treated as free functions with an extra argument • Warning, not tested (works with boost): struct X { intfoo(int); }; function<int (X*, int)> f; f = &X::foo; // pointer to member X x; int v = f(&x, 5); // call X::foo() for x with 5 function<int (int)> ff = std::bind(f,&x,_1); // first argument for f is &x v=ff(5); // call x.foo(5)
20.7.12.1.3 bind // general binder for functions and function objects: template<CopyConstructible F, CopyConstructible... BoundArgs> unspecified bind(F f, BoundArgs... bound_args); int f(int,char,double); auto ff = bind(f,_1,’c’,1.2); // deduce return type int x = ff(7); // f(7,’c’,1.2); // “legacy” binder for functions objects : template<Returnable R, CopyConstructible F, CopyConstructible... BoundArgs> unspecified bind(F f, BoundArgs... bound_args); auto f2 = bind<int>(f,7,’c’,_1); // explicit return type int x = f2(1.2); // f(7,’c’,1.2); namespace placeholders { //M is the implementation-defined number of placeholders extern unspecified _1; extern unspecified _2; // ... }
20.7.12.1.3 bind • Many a party trick int f(string,int); auto ff = bind(f,_2,_1); f(“hello”,10); ff(10,”hello”); • Can’t directly handle overloading int g(int); double g(double); auto g1 = bind(g,_1); // error: which g()? auto g2 = bind((double(*)(double))g,_1); // ok (but ugly)
Bind and function • Standard 20.7 Function objects • Herb Sutter: Generalized Function Pointers. August 01, 2003 • http://www.ddj.com/article/printableArticle.jhtml;jsessionid=QQIFSNAIOYXN0QSNDLPSKHSCJUNN2JVN?articleID=184403746&dept_url=/cpp/ • Douglas Gregor : Boost.Function. 2001-2004 • http://www.boost.org/doc/libs/1_38_0/doc/html/function.html
23 Containers library • Sequence • <array> // C++0x • <deque> • <forward_list> // C++0x • <list> • <queue> • <stack> • <vector> • Associative • <map> • <set> • Unordered associative • <unordered_map> // C++0x • <unordered_set> // C++0x
23.3.1 Class template array • Fixed length array • like built-in array, but • No nasty pointer decay • Knows its size • Has STL interface (e.g. begin() and end(), assignment, and pass-by-value) • array<int, 6> a1= { 1, 2, 3 }; • array<int> a2 = { 1, 2, 3 }; // error: size unknown/missing • Zero-length array • array<int,0> a0; // ok
23.3.1 Class template array template <ValueType T, size_t N > requires NothrowDestrucbible<T> struct array { // types: typedef T & reference; typedef const T & const_reference; typedefimplementation defined iterator; typedefimplementation defined const_iterator; typedefsize_tsize_type; typedefptrdiff_tdifference_type; typedef T value_type; typedefreverse_iterator<iterator> reverse_iterator; typedefreverse_iterator<const_iterator> const_reverse_iterator; T elems[N]; // exposition only // … };
23.3.1 Class template array template <ValueType T, size_t N > requires NothrowDestrucbible<T> struct array { // … // No explicit construct/copy/destroy for aggregate type requiresCopyAssignable<T> voidfill(const T& u); requiressSwappable<T> void swap(array<T, N> &); // can swap, of course // iterators: iterator begin(); const_iterator begin() const; iterator end(); const_iterator end() const; reverse_iteratorrbegin(); const_reverse_iteratorrbegin() const; reverse_iterator rend(); const_reverse_iterator rend() const; const_iteratorcbegin() const; const_iteratorcend() const; const_reverse_iteratorcrbegin() const; const_reverse_iteratorcrend() const; // … };
23.3.1 Class template array template <ValueType T, size_t N > requires NothrowDestrucbible<T> struct array { // … // capacity: constexprsize_type size() const; constexprsize_typemax_size() const; constexprbool empty() const; //element access: reference operator[](size_type n); const_reference operator[](size_type n) const; const_reference at(size_type n) const; reference at(size_type n); reference front(); const_reference front() const; reference back(); const_reference back() const; T * data(); // get a pointer to elements const T * data() const; };
Array and tuple • Obviously array<int,3> is basically the same as tuple<int,int,int> • i.e. an array is a homogenous tuple (if that’s how you want to look at it) array<int,3> a = { 11, 22 ,33 }; int x = get<2>(a); // a[2]
23.3.3 Class template forward_list • A forward_list is a container that supports forward iterators and allows constant time insert and erase operations anywhere within the sequence, with storage management handled automatically. Fast random access to list elements is not supported. • [ Note: It is intended that forward_list have zero space or time overhead relative to a hand-written C-style singly linked list. Features that would conflict with that goal have been omitted.—end note ] • A forward_list satisfies all of the requirements of a container (table 80), except that the size() member function is not provided. Descriptions are provided here only for operations on forward_list that are not described in that table or for operations where there is additional semantic information. template <ValueType T, Allocator Alloc = allocator<T> > requires NothrowDestructible<T> class forward_list { // no size };
Lists • Should a list have size() be O(1)? • Everybody’s first answer is Yes! • If so every list gets one more word • The empty list doubles in size • There are lots of empty lists! • Alternative (for all containers): • Trade time for space • And hope caching will take care of the time container Descriptor elem size element
std::vector – what’s new? • What’s new in the language? • Concepts • Initializer_list • Rvalue references • Variadic templates • constexpr (not for vector; nothing is perfect ) • Plus real allocator objects
Allocators • Key issues: • who controls the allocation of subsidiary data, the container or the element? • E.g. characters for vector<string> • C++98: the element (each individual type controls its own sub-allocations) • C++0x: your choice: allocators are passed along. • When you copy, is the allocator copied? • Do I need to set aside memory for an allocator in each object that allocates anything? • E.g., vector, string, list • “neutered” for C++98 • No allocator objects • “full blown” “scoped allocators” for C++0x • 20.8.1-7, 21 pages • C++98 “legacy allocators” work • You can have allocators with virtual functions and all
Allocators • Standard 20.8.1-7 • [N1850=05-0110] Pablo Halpern: Towards a Better Allocator Model • http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2005/n1850.pdf • [N2479=07-0349] John Lakos: Normative Language to Describe Value Copy Semantics • http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2007/n2479.pdf • [N2387=07-0247] Pablo Halpern: Omnibus Allocator Fix-up Proposals • http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2007/n2387.pdf • [N2525=08-0035] Pablo Halpern: Allocator-specific Swap and Move Behavior • http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2008/n2525.pdf • [N2554=08-0064] Pablo Halpern: The Scoped Allocator Model (Rev 2) • http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2008/n2554.pdf • And several other documents by Pablo that year • [N2840=09‑0030] Pablo Halpern: Defects and Proposed Resolutions for Allocator Concepts (Rev 2) • http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2009/n2840.pdf
std::vector – what’s new? template <ValueType T, Allocator Alloc = allocator<T> > requires MoveConstructible<T> // at least move class vector { public: //… requires AllocatableElement<Alloc, T> explicit vector(size_type n); requiresAllocatableElement<Alloc, T, T&&> vector(vector&&); requiresAllocatableElement<Alloc, T, const T&> vector(initializer_list<T>, const Allocator& = Allocator()); // { … } ~vector(); // note: not virtual, of course // … };
std::vector – what’s new? template <ValueType T, Allocator Alloc = allocator<T> > requires MoveConstructible<T> class vector { public: //… void shrink_to_fit(); // you don’t have to know/use the swap() trick // … template <class... Args> requires AllocatableElement<Alloc, T, Args&&...> void emplace_back(Args&&... args); // construct in place requiresAllocatableElement<Alloc, T, const T&> voidpush_back(const T& x); requiresAllocatableElement<Alloc, T, T&&> voidpush_back(T&& x); // move void pop_back(); // … void swap(vector<T,Alloc>&&); // note: && void clear(); };
std::vector – what’s new? • Moving element is a major cost in many algorithms • vector<string> v; • String s = "Doctor Who"; • v.push_back(s); // must copy (expensive) • v.push_back("Doctor Who"); // can move rvalue (much cheaper) • v.emplace_back("Doctor Who"); // build string in place (cheapest) • You’ll need to look at real performance data to know if there is a significant different between the last two cases • Note that you don’t actually need more than one argument for emplace() • Wikipedia: • Emplace • Verb • to emplace (third-person singular simple present emplaces, present participle emplacing, simple past and past participle emplaced) • To assign a position to something, or to locate something at a particular place
std::vector – what’s new? template <ValueType T, Allocator Alloc = allocator<T> > requires MoveConstructible<T> class vector { public: //… requiresAllocatableElement<Alloc, T, const T&> && CopyAssignable<T> vector<T,Alloc>& operator=(const vector<T,Alloc>& x); requiresAllocatableElement<Alloc, T, T&&> && MoveAssignable<T> vector<T,Alloc>& operator=(vector<T,Alloc>&& x); requiresAllocatableElement<Alloc, T, const T&> && CopyAssignable<T> vector<T,Alloc>& operator=(initializer_list<T>); // … };
Summary • C++0x is too BIG and complex • E.g., Concepts, allocators, localization (fortunately localized), “old stuff and C compatibility” • The first C++ manual was 66 pages, the C++0x standard is 1347 pages and growing • Unfair comparison, but … • I’m more worried about “complex” than by “BIG” • If feels like a new language • Much better, smoother, expressive abstraction facilities • Better integration of features • Fewer frustrations, terser and clearer code • You can write better code in C++0x than in C++98 • There are far too few standard libraries • Unicode, XML, distributed computing, linear algebra