Presentation is loading. Please wait.

Presentation is loading. Please wait.

STL11: Magic && Secrets Stephan T. Lavavej ("Steh-fin Lah-wah-wade")

Similar presentations


Presentation on theme: "STL11: Magic && Secrets Stephan T. Lavavej ("Steh-fin Lah-wah-wade")"— Presentation transcript:

1 STL11: Magic && Secrets Stephan T. Lavavej ("Steh-fin Lah-wah-wade")
Visual C++ Libraries Developer Version February 2, 2012

2 Magic Version February 2, 2012

3 Malevolent Architecture
Space How much RAM does your phone/tablet/console have? How much RAM does your server have? How many users does it handle? Is it virtualized? How much L1/L2/L3 cache do you have? Time How many cores do you have? How fast is each one? How many milliseconds do you have at 60 FPS? Abstraction How big are the problems you solve? Are they getting bigger? How smart are you? Are you getting smarter? How many programmers/end-users use the STL? All the users, all of them Version February 2, 2012

4 Visible Invisibility Where did the deleter/allocator types go?
// shared_ptr, how do I construct thee? Let me count the ways: shared_ptr<T> sp1(new T(args)); shared_ptr<T> sp2(new T(args), del); shared_ptr<T> sp3(new T(args), del, alloc); auto sp4 = make_shared<T>(args); auto sp5 = allocate_shared<T>(alloc, args); // Plus other "non-fundamental" ways Where did the deleter/allocator types go? vector<T, MyAlloc<T>> versus shared_ptr<T> Magic: type erasure! Less visibly, but more importantly, also powering make_shared<T>() Version February 2, 2012

5 Exposition Diagram _Uses: 7 _Weaks: 4 _Ptr _Ptr 1729 _Dtor _Rep
_Ref_count_del <_Ty, _Dx> _Ptr _Rep shared_ptr<int> _Ref_count_base _Uses: 7 _Weaks: 4 _Ref_count<_Ty> 1729 int _Uses: 7 _Weaks: 4 _Ptr _Dtor _Myal _Ref_count_del_alloc <_Ty, _Dx, _Alloc> Type erasure: _Ref_count_base * _Rep points to _Ref_count<_Ty>, etc. Version February 2, 2012

6 A Wizard Did It _Uses: 7 _Weaks: 4 _Ptr _Ptr _Rep _Uses: 7 _Weaks: 4
shared_ptr<int> _Ref_count_base _Uses: 7 _Weaks: 4 _Storage: 1729 _Ref_count_obj<_Ty> _Uses: 7 _Weaks: 4 _Ptr _Ref_count<_Ty> _Uses: 7 _Weaks: 4 _Storage: 1729 _Myal _Ref_count_obj_alloc <_Ty, _Alloc> No _Ptr! We know where you live. Version February 2, 2012

7 When Things Spin, Science Happens
Actual measurements on Win7 Vista+: Low Fragmentation Heap automatically enabled Including allocator overhead Excluding shared_ptr itself (x86: 8 bytes; x64: 16) Original recipe shared_ptr<int> x86: 40 bytes (x64: 48) – Boost , GCC 4.6.1, VC10 SP1 Extra crispy make_shared<int>() x86: 32 bytes (x64: 48, unchanged) – Boost , GCC 4.6.1 x86: 24 bytes (x64: 32) – VC10 SP1 Reducing 2 dynamic memory allocations to 1 is good Saving space is also good Two great tastes that taste great together Version February 2, 2012

8 Take A Third Option Why is copying slower than moving?
Copying does more work Allocating memory Copying elements (recursively!) _InterlockedIncrement() Is moving instantaneous? Not quite; it does some work Copying pointers Nulling source pointers Copying other Plain Old Data Also, there are throwing move ctors (shock! horror!) New option: emplacement Version February 2, 2012

9 Computers Are Fast vector<pair<string, int>> v; v.emplace_back("Carmichael", 3 * 11 * 17); v.emplace_back( "Hardy", 1 * 1 * * 12 * 12); v.emplace_back( "Ramanujan", 9 * 9 * * 10 * 10); // vector<T> member functions: void push_back(const T& t); void push_back(T&& t); template <typename... Args> void emplace_back(Args&&... args); iterator insert(const_iterator pos, const T& t); iterator insert(const_iterator pos, T&& t); iterator emplace(const_iterator pos, Args&&... args); Version February 2, 2012

10 Nice Job Breaking It, Hero
N3035 (Feb. 16, 2010) [pairs.pair]/4-5: pair(const T1& x, const T2& y); Effects: The constructor initializes first with x and second with y. template<class U, class V> pair(U&& x, V&& y); Effects: The constructor initializes first with std::forward<U>(x) and second with std::forward<V>(y). VC10 SP1: (remember that NULL is 0) pair<X *, double> p1(0, 3.14); // BOOM error C2440: 'initializing' : cannot convert from 'int' to 'X *' Conversion from integral type to pointer type requires reinterpret_cast, C-style cast or function-style cast pair<X *, double> p2(nullptr, 3.14); // OK Version February 2, 2012

11 Set Right What Once Went Wrong
N [pairs.pair]/5, 7-8: pair(const T1& x, const T2& y); Effects: The constructor initializes first with x and second with y. template<class U, class V> pair(U&& x, V&& y); Effects: The constructor initializes first with std::forward<U>(x) and second with std::forward<V>(y). Remarks: If U is not implicitly convertible to first_type or V is not implicitly convertible to second_type this constructor shall not participate in overload resolution. VC11: pair<X *, double> p1( , 3.14); // OK pair<X *, double> p2(nullptr, 3.14); // OK nullptr is still better; down with 0/NULL Consider make_shared<T>(args) SFINAE: a truly marvelous technique, which this slide is too small to explain Version February 2, 2012

12 Benevolent Architecture
void meow(const pair<int, int>& p) { cout << "int: " << p.first << ", " << p.second << endl; } void meow(const pair<string, string>& p) { cout << "string: " << p.first << ", " << p.second << endl; meow(make_pair(8191, 65537)); meow(make_pair("Mersenne", "Fermat")); Invalid C++98/03! VC9 SP1, VC10 SP1: error C2668: 'meow' : ambiguous call to overloaded function Valid C++11! VC11: int: 8191, 65537 string: Mersenne, Fermat Version February 2, 2012

13 Drop The Hammer N3337 20.3.2 [pairs.pair]/11, 14:
template<class U, class V> pair(const pair<U, V>& p); Remark: This constructor shall not participate in overload resolution unless const U& is implicitly convertible to first_type and const V& is implicitly convertible to second_type. template<class U, class V> pair(pair<U, V>&& p); Remark: This constructor shall not participate in overload resolution unless U is implicitly convertible to first_type and V is implicitly convertible to second_type. const char * is not implicitly convertible to int const char * is implicitly convertible to string Not quite perfect – SFINAE is a blunt hammer Consider Base *, Derived *, and MoreDerived * Version February 2, 2012

14 Secrets Version February 2, 2012

15 To The Future And Beyond
VC11 Beta/RTM: <condition_variable>, <future>, and <mutex> are powered by the Concurrency Runtime ConcRT is highly efficient; read about it on MSDN Creating lots of futures won't create lots of physical threads – just enough to saturate your user's cores VC11 Beta/RTM: <atomic>, <condition_variable>, <future>, <mutex>, and <thread> all #error under /clr[:pure] Same for ConcRT in VC10/VC11 Version February 2, 2012

16 Twenty Minutes Into The Future
Move semantics string flip(string s) { reverse(s.begin(), s.end()); return s; } int main() { vector<future<string>> v; v.push_back(async([] { return flip( " ,olleH"); })); v.push_back(async([] { return flip(" evitaNgnioG"); })); v.push_back(async([] { return flip( "\n!2102"); })); for (auto& e : v) { cout << e.get(); future Right angle brackets Lambdas async auto Hello, GoingNative 2012! Range-based for-loop Version February 2, 2012

17 The Reveal The range-based for-loop will be supported in... ... VC11!
... VC11 Beta! Developer: Jonathan Caves Tester: Stephan T. Lavavej Fake tester, real tests Minions: Ryan Molden, Ahmed Charles, Pavel Minaev Reviewers: Ulzii Luvsanbat, Andy Rich Intellisense: VC11 RTM In Beta, you'll get red squiggles, but it'll compile Version February 2, 2012

18 All There In The Manual Condensing N3337 6.5.4 [stmt.ranged]/1:
for (for-range-declaration : expression) statement Is equivalent to: { auto&& __range = (expression); for (auto __begin = begin-expr, __end = end-expr; __begin != __end; ++__begin) { for-range-declaration = *__begin; statement } braced-init-lists are allowed instead of expressions But not in VC11 Beta/RTM __range, __begin, and __end are "for exposition only" Version February 2, 2012

19 Imported Alien Phlebotinum
auto&& __range = (expression); auto is powered by template argument deduction auto&& behaves like T&& in perfect forwarding auto&& binds to everything auto&& becomes X&, const X&, X&&, or const X&& __range is a named reference Keeps temporaries alive Doesn't copy anything This works perfectly: vector<int> func(); for (int i : func()) { cout << i << endl; } Version February 2, 2012

20 Sealed Evil In A Can begin-expr and end-expr depend on the expression
auto&& __range = (expression); for (auto __begin = begin-expr, __end = end-expr; begin-expr and end-expr depend on the expression For arrays of N elements: __range and __range + N For classes with begin/end members: __range.begin() and __range.end() For everything else: begin(__range) and end(__range) begin/end are found by Argument-Dependent Lookup Version February 2, 2012

21 Red Herring For generic programming, <iterator> provides:
template <typename C> auto begin( C& c) -> decltype(c.begin()); template <typename C> auto begin(const C& c) -> decltype(c.begin()); template <typename C> auto end( C& c) -> decltype(c.end()); template <typename C> auto end(const C& c) -> decltype(c.end()); template <typename T, size_t N> T * begin(T (&array)[N]); template <typename T, size_t N> T * end(T (&array)[N]); But range-for never uses them! Range-for works with, but doesn't require, the STL C++/CX: range-for requires <collection.h>, which provides begin()/end() for WFC::IVector<T>^, etc. Version February 2, 2012

22 Pop Quiz for-range-declaration = *__begin; for (string s1 : v1)
s1 is a modifiable copy! v1 can be modifiable/const for (const string s2 : v2) s2 is a const copy! v2 can be modifiable/const for (string& s3 : v3) Observe/modify s3 in-place! v3 must be modifiable for (const string& s4 : v4) Observe s4 in-place! v4 can be modifiable/const Version February 2, 2012

23 Invisible Subtle Difference
for-range-declaration = *__begin; for (auto e5 : v5) Same: e5 is a modifiable copy, v5 can be modifiable/const e5 is modifiable even when v5 is const; auto drops constness for (const auto e6 : v6) Same: e6 is a const copy, v6 can be modifiable/const for (auto& e7 : v7) Observe/modify e7 in-place, if v7 is modifiable! Observe e7 in-place, if v7 is const! e7 is const when v7 is const; auto& preserves constness for (const auto& e8 : v8) Same: observe e8 in-place, v8 can be modifiable/const auto is powered by template argument deduction C++98/03: foo(T5 t5) drops constness, bar(T7& t7) preserves it Version February 2, 2012

24 I Will Only Slow You Down
vector<string> v; // Which one is faster? for (const auto& s1 : v) for (const string& s2 : v) // They're identical! map<string, int> m; // Which one is faster? for (const auto& p1 : m) for (const pair<string, int>& p2 : m) // const auto& p1 is faster! // m's value_type is pair<const string, int> // Binding const pair<string, int>& to pair<const string, int> // constructs a temporary pair<string, int>, // which copies a string. Version February 2, 2012

25 If My Calculations Are Correct
95% of the time, you should use: for (auto& e : r) for (const auto& e : r) Why would you want to use anything else? When you actually want a copy for (auto e : r) // intentional copy When you actually want a conversion for (uint64_t x : r) // uint32_t => uint64_t When proxies might be involved for (auto&& e : r) // binds to everything Examples: vector<bool> and <collection.h> Version February 2, 2012

26 That, Detective, Is The Right Question
N3337, the first post-C++11 Working Paper open-std.org/jtc1/sc22/wg21/docs/papers/2012/n3337.pdf "The only changes since [C++11] are editorial." C++11 Features In VC11 blogs.msdn.com/b/vcblog/archive/2011/09/12/ aspx VC10's ConcRT documentation msdn.microsoft.com/en-us/library/dd aspx My address Version February 2, 2012

27 Bonus Slides Version February 2, 2012

28 Color Coded For Your Convenience
template <typename T> struct Vector { void Assign(size_t, const T&); template <typename InIt> typename enable_if<!is_integral<InIt>::value, void>::type Assign(InIt, InIt); Vector(size_t, const T&); template <typename InIt> Vector(InIt, InIt, void **>::type = nullptr); }; int * p = nullptr; Vector<int> v1(17, 29); v1.Assign(17, 29); Vector<int> v2(p, p); v2.Assign(p, p); Version February 2, 2012

29 Magical Incantation template <typename... Types> struct Tuple { explicit Tuple(const Types&...); template <typename... Other> explicit Tuple(Other&&..., typename enable_if<COND, void **>::type = nullptr); // BAD }; N [temp.deduct.call]/1: "For a function parameter pack that does not occur at the end of the parameter-declaration-list, the type of the parameter pack is a non-deduced context." template <typename... Other, typename Dummy = typename enable_if<COND, void>::type> explicit Tuple(Other&&...); // GOOD Version February 2, 2012

30 Did Not Do The Research Core Issue 1442: "Normal ADL" or "pure ADL"?
N [stmt.ranged]/1: "begin and end are looked up with argument-dependent lookup" 3.4.2 [basic.lookup.argdep]/3: "Let X be the lookup set produced by unqualified lookup (3.4.1) and let Y be the lookup set produced by argument dependent lookup (defined as follows). If X contains a declaration of a class member, or a block-scope function declaration that is not a using-declaration, or a declaration that is neither a function or a function template then Y is empty. Otherwise Y is the set of declarations found in the namespaces associated with the argument types as described below. The set of declarations found by the lookup of the name is the union of X and Y." Normal ADL: range-for would consider local using-directives, etc. Pure ADL: range-for's ADL would still work within member functions of classes that have their own begin/end members VC11 Beta: normal ADL; clang/EDG/GCC: pure ADL; VC11 RTM: ? Version February 2, 2012


Download ppt "STL11: Magic && Secrets Stephan T. Lavavej ("Steh-fin Lah-wah-wade")"

Similar presentations


Ads by Google