Entries:
Comments:
Posts:

Loading User Information from Channel 9

Something went wrong getting user information from Channel 9

Latest Achievement:

Loading User Information from MSDN

Something went wrong getting user information from MSDN

Visual Studio Achievements

Latest Achievement:

Loading Visual Studio Achievements

Something went wrong getting the Visual Studio Achievements

C9 Lectures: Stephan T. Lavavej - Standard Template Library (STL), 9 of n

Download

Right click “Save as…”

Welcome to another installment of C9 Lectures covering the powerful general C++ library, STL. Joining us once again is the great Stephan T. Lavavej, Microsoft's keeper of the STL cloth (this means he manages the partnership between the owners of STL (dinkumware) and Microsoft, including, of course, bug fixes and enhancements to the STL that ships as part of Visual C++). Simply, Stephan is a C++ library developer.

As is Stephan's nature, he elaborates on technical details in very substantive way. The Standard Template Library, orSTL, is a C++ library of container classes, algorithms, and iterators. STL provides many fundamental algorithms and data structures. Furthermore, the STL is a general-purpose library: its components are heavily parameterized, such that almost every component in the STL is a template.

In part 9, Stephan introduces us to rvalue references, a C++0x language feature, which enables two different things: move semantics and perfect forwarding. It's all about performance, brothers and sisters!

Enjoy! Learn!

Books mentioned by Stephen:

The C++ Standard Library: A Tutorial And Reference by Nicolai M. Josuttis
Effective STL by Scott Meyers

[STL Introduction lecture links]

Part 1 (sequence containers)

Part 2 (associative containers)

Part 3 (smart pointers)

Part 4 (Nurikabe solver) - see Wikipedia's article and Stephan's updated source code

Part 5 (Nurikabe solver, continued)

Part 6 (algorithms and functors)

Part 7 (algorithms and functors, continued)

Part 8 (regular expressions)

Part 9 (rvalue references)

Part 10 (type traits)

Tags:

Follow the Discussion

  • nonenone

    great series!!! i have learned allot more about the STL. and appreciate the detail...

  • Excellent. WOW!!

    Thank you, Stephan and Charles.

    Good Lecture and Video.

  • 4 years ago, many people frequently asked me.

    "Why C++ not Java or C#, Why template, Why? Why just Freaky??"

    I still write and develop with C++ and Template.

    And now I will answer that.Smiley

  • Can't we simplify the move assignment operator as follows?

    remote_integer& operator=(remote_integer&& other)
    {
        swap(other);
        return *this;
    }

    I mean, swap is exception-safe anyway, right? Why the extra temporary?

  • Awesome video! The only thing I missed was discussing what T&& means when T is a template parameter, but maybe explaining reference collapsing rules would have taken too much time... anyway, what will the next video be about? Wink

  • STLSTL

    [NotFredSafe]
    > Can't we simplify the move assignment operator as follows?
    > remote_integer& operator=(remote_integer&& other) { swap(other); return *this; }
    > I mean, swap is exception-safe anyway, right? Why the extra temporary?

    That's subtly different. In my implementation, the assignment destination's original resources are destroyed immediately. In your implementation, the assignment destination's original resources are swapped into the assignment source. This doesn't usually matter for two reasons:

    1. When the assignment source is a temporary, it's going to be destroyed very soon ("at the semicolon"). However, it could be std::move(lvalue), in which case it might survive for a long time until reassignment or destruction.

    2. When the resources consist purely of memory, usually nobody will notice if we hold on to memory a little longer than absolutely necessary. However, the memory involved could be significant, or non-memory resources could easily be involved.

    Because I prefer to avoid subtle gotchas, I implement move assignment so that, like copy assignment, it immediately destroys the assignment destination's original resources.

    > The only thing I missed was discussing what T&& means when T is a template parameter,
    > but maybe explaining reference collapsing rules would have taken too much time...

    I would have liked to get to perfect forwarding, but I ran out of time. 45 minutes isn't very much time.

    > anyway, what will the next video be about?

    I'm open to suggestions. I'd like to cover simple techniques before advanced ones (looking at the STL's implementation would be advanced). I think I might demonstrate <type_traits>. (Note that I won't be covering iostreams in this series.)

  • > anyway, what will the next video be about?

    I'm open to suggestions. I'd like to cover simple techniques before advanced ones (looking at the STL's implementation would be advanced). I think I might demonstrate <type_traits>. (Note that I won't be covering iostreams in this series.)

     

    #include <type_traits>, decltype, and the rest of the template metaprogramming (aka using C++ to "preprocess"/modify/script C++ during compile-time) material would be interesting.  Any info on how to use std::string or char * ... or whatever ... for manipulating strings at compile-time in C++0x would be appreciated.  If the new standard still does not support manipulating strings at compile-time without work-arounds (aka boost::mpl::string), then what is the current justification that the standards committee gives for still not allowing string manipulations at compile-time?  For template-metaprogramming, I'd personnally like to see an example of loop-unrolling ... right now, if I need a for ( i = 0; i < n; ++i ) { v[i]=...; } but I just want a v[0]=...; v[1]=...; ...; v[n]=...;, then I just script that unrolled-loop to a text file and copy & paste ... which works ... but is a kludge.  I'd like C++ to do that loop-unrolling for me at compile-time ... so I can share and reuse that C++ solution.

     

    Also, generating random floats or doubles from a Normal distribution using #include <random> would be great ... esp. for simultations, stocastic models, and games.  Any explanation on how to create our own distributions would also be great.

     

    Discussing the usefulness, pros and cons, of std::valarray and std::slice from #include <valarray> ... and showing off some of the MATLAB-like functionality of std::valarray (like valarray1 + valarray2, pow( valarray1, 2. ), valarray1.sum(), valarray1.apply( user_function ), etc.)

     

    Finally, simultaneously returning multiple values from a function or method using #include <tuple> and then accessing those values ... esp. best practices. 

     

    Thanks In Advance,

    Joshua Burkholder

     

  • Another great video, thanks so much for making this information available for developers.

    Posted 23 hours ago and almost 12,000 views! Fantastic Job!

     

  • Eric AguiarHeavens​Revenge Know Thyself

    Actually STL, to me the usage of lvalues and rvalues being referred to as left & right makes sense when thought of to be similar to key-value pairs. With the left lvalue being the "key" and the rvalue being the "value" which lives at the lvalue. In this way of thinking about them, I will try to explain using a table, where lvalues are user defined and named, and the rvalues are the compiler managed values which are refrenced internally as temporaries with lifetimes managed by the compiler.

    User defined named; lvalues which is our explicitly given name for a location:

    -------------------
    | lvalue | rvalue |
    -------------------
    | cat1   | fred   |
    | cat2   | jerry  |
    | cat3   | steve  |
    -------------------

    Compiler defined temporaries; rvalues given temporary named locations with their lifetimes managed by the compiler:
    -------------------
    | lvalue | rvalue |
    -------------------
    | temp1  |   11   |
    | temp2  |   22   |
    | temp3  |   33   |
    -------------------


    As you can see, we can refer to our managed/uer-defined variables using our named location(lvalue) while on the other hand, the compiler generated temporaries arent usually cared about, where we only want their values that they contain(its rvalue). Even though lvalue could also mean location value and rvalue meaning return value, whichever way you like best, it IS possible to think of them being left & right in my opinion.

    I must say, STL this video was the video I enjoyed the most of your 9 videos, even though it wasn't specifically aimed at an STL feature, because you finally delved into a more technical subject making this valuable.

    Do you also think this makes sense??

  • New2STLNew2STL

    Nice videos, I'm enjoying how clear and relaxed you teach the STL.
    Will be a nice have a indeph of <type_traits> for next video. The only time I ventured in <type_traits> was to use the aligned_storage class, the compiler declspec(align(#)), and <array> with the SSE intrinsics in some tests and it worked.

  • @STL:  Your description of not being able to use & on the temporary created by std::string concatenation doesn't hold for C++ in Visual Studio 2010.  For instance, when I compile the following code ...

    #include <iostream>
    using std::cout;
    using std::endl;
    
    #include <string>
    using std::string;
    
    int main () {
        string x( "hello " );
        string y( "world" );
        cout << ( x + y ) << endl;
        cout << &( x + y ) << endl;
        return 0;
    }
    
    ... there are no compiler warnings and no compiler errors.  When I run that compiled code, I get the following output:
    hello world
    002FFD54
    Aside:  Using g++ 4.5.0 with -std=c++0x, there is a compiler warning, but no compiler errors.  Here is the compiler warning:
    warning: taking address of temporary

     

    If I change std::string to int, then everything works as expected and I get the following compilation error for C++ in Visual Studio 2010:

    error C2102: '&' requires l-value
    .  In g++ 4.5.0, I get the following compilation error:
    error: lvalue required as unary '&' operand

     

    Joshua Burkholder

  • @Burkholder:Disregard my no-compiler-warning for C++ in Visual Studio 2010.  I was compiling with Warning Level Level3, vice Warning Level Level4.  Once I switched to Level4, I received the following warning

    warning C4238: nonstandard extension used : class rvalue used as lvalue
    Still no compiler error though, but at least this is the same behavior as g++.

     

    Joshua Burkholder

  • STLSTL

    [HeavensRevenge]
    > where lvalues are user defined and named, and the rvalues are the compiler managed values
    > which are refrenced internally as temporaries with lifetimes managed by the compiler.

    Sorry, this is deeply incorrect.

    As I explained in the video, lvalueness/rvalueness is a property of *expressions*, not objects. It's possible to have lvalue expressions referring to temporary objects (for example, this happens whenever a temporary X is passed to a function taking const X&), and to have rvalue expressions referring to user-named objects (for example, std::move(foobar)).

    > Even though lvalue could also mean location value and rvalue meaning return value, whichever way you like best

    Historically, L meant "left side of an assignment" and R meant "right side of an assignment". As I explained in the video, this is no longer accurate (due to constness, among other things). Nor should you attempt to invent your own meanings for L and R.

    I think of them like this: lvalues are "persistent" and rvalues are "ephemeral". I can say a[i] over and over, referring to something persistent, so it is an lvalue. Given const X& x, I can also say x over and over, so it is an lvalue. It doesn't matter if x is bound to a temporary object that will evaporate shortly after my function returns. For the duration of my function where x is in scope, I have something persistent. Conversely, y + z does not refer to something persistent, so it is an rvalue. std::move(foobar) is also an rvalue, because it's saying "I want you to pretend that foobar is going to evaporate soon".

    > I must say, STL this video was the video I enjoyed the most of your 9 videos,
    > even though it wasn't specifically aimed at an STL feature, because you
    > finally delved into a more technical subject making this valuable.

    Thanks.

    [Burkholder]
    > Disregard my no-compiler-warning for C++ in Visual Studio 2010.

    I recall mentioning VC's Evil Extension in the video, and exhorting the audience to compile with /W4. If I didn't, I should have. This extension is the most disgusting, vile thing ever (the Standard prohibits this nonsense for extremely good reasons), it should never be used, and it's good that /W4 warns about it.

  • Any particular reason you did not mention the distinction between prvalues and xvalues?

    T prvalue();
    T& lvalue();
    T&& xvalue();

    I think this is important because the xvalue "std::move(var)" is an rvalue even though it allows you to refer to the same object multiple times (as opposed to prvalues such as "x+y").

  • I am mostly ignorant of C++ (and even more so of 0X), so please forgive me if this makes no sense.

    It seems to me that what is accomplished by "std::move" could (and perhaps should) be implied by how the code is written. Cannot the compiler reason that "here is the last place this reference is...referenced, therefore it is safe to use "move" semantics"?

    Furthermore, supposing you specify "std::move", could not the compiler issue a strong warning or even an error, should the 'moved' reference be...referenced at a later point?

  • obviousobvious

    [quote]
    1 day ago, STL wrote
    [NotFredSafe]> Can't we simplify the move assignment operator as follows?> remote_integer& operator=(remote_integer&& other) { swap(other); return *this; }> I mean, swap is exception-safe anyway, right? Why the extra temporary?
    That's subtly different. In my implementation, the assignment destination's original resources are destroyed immediately. In your implementation, the assignment destination's original resources are swapped into the assignment source. This doesn't usually matter for two reasons:
    1. When the assignment source is a temporary, it's going to be destroyed very soon ("at the semicolon"). However, it could be std::move(lvalue), in which case it might survive for a long time until reassignment or destruction.
    2. When the resources consist purely of memory, usually nobody will notice if we hold on to memory a little longer than absolutely necessary. However, the memory involved could be significant, or non-memory resources could easily be involved.
    Because I prefer to avoid subtle gotchas, I implement move assignment so that, like copy assignment, it immediately destroys the assignment destination's original resources.
    [/quote]

     
    Sorry, I'm very confused, wont that extra temporary cause performance loss, like an extra copy or does the compiler optimize away this extra temporary ?
     
    Thanks for another great lecture, STL, i'm already eagerly waiting for more. :)

  • , dpratt71 wrote

    It seems to me that what is accomplished by "std::move" could (and perhaps should) be implied by how the code is written. Cannot the compiler reason that "here is the last place this reference is...referenced, therefore it is safe to use "move" semantics"?

    If you return a local variable, the move is indeed implied.

    , dpratt71 wrote

    Furthermore, supposing you specify "std::move", could not the compiler issue a strong warning or even an error, should the 'moved' reference be...referenced at a later point?

    Depends on what you mean by "referenced at a later point". Consider how std::swap is implemented:

    template <typename T>
    void swap(T& a, T& b)
    {
      T c = std::move(a);
        a = std::move(b);
        b = std::move(c);
    }
    

  • New2STLNew2STL

    I found a text made by Thomas Becker a good companion while re-watching de video, it is dated from July/2010, the text have 10 pages and try to explain de rvalue, move, and forward step by step.[url]http://thbecker.net/articles/rvalue_references/section_01.html[/url]

  • STLSTL

    [NotFredSafe]
    > Any particular reason you did not mention the distinction between prvalues and xvalues?

    I suspected that explaining the lvalue/xvalue/prvalue/glvalue/rvalue taxonomy would be more confusing than clarifying.

    [dpratt71]
    > Cannot the compiler reason that "here is the last place this reference is...referenced, therefore it is safe to use "move" semantics"?

    The compiler is very smart, but not omniscient. In particular, when it sees something like a memory allocation followed by a deallocation, it can't determine whether that work is unnecessary.

    C++ compilers have always understood the distinction between lvalues and rvalues (in fact, even C compilers have this knowledge). What move semantics does is very clever: it provides just a little bit of extra information to the compiler in order to enable major optimizations that were previously impossible. This information takes two forms: telling the compiler to treat certain lvalues as rvalues with std::move(), and telling the compiler to do something more efficient during construction and assignment when the source is a modifiable rvalue.

    > Furthermore, supposing you specify "std::move", could not the compiler issue a strong
    > warning or even an error, should the 'moved' reference be...referenced at a later point?

    In general, moved-from objects are in a "valid but unspecified" state, so the only things that you should do to them are reassign them or destroy them. However, certain types provide stronger guarantees. For example, moved-from shared_ptrs are guaranteed to be empty.

    [obvious]
    > Sorry, I'm very confused, wont that extra temporary cause performance loss

    It should not. The optimizer should be able to see through simple pointer assignments. In any event, this is just a matter of a few instructions. The major expense (unnecessary dynamic memory allocation/deallocation) has already been avoided.

    [NotFredSafe]
    > If you return a local variable, the move is indeed implied.

    Yes, due to a special rule in the Working Paper. Named local variables are lvalues, and ordinarily lvalues must be copied from, not moved from. However, returning from a function triggers the destruction of local variables. Since val's destruction is imminent, "return val;" will automatically move from val.

  • Please make the other video before Christmas (it'll be considered your gift to us Wink ). Well, I'm still in for the Template Metaprogramming, but don't start too complicated, since I'm not that good at math.

    Question, Video 3 you had the container traits etc. Since I'm not only using VS2010, I can't always use the "auto" keyword. How do I get to the iterator from the template argument? Will this work? It compiles, but I can't say if it can crash:

    template <typename Container, typename Predicate>
    void erase_if_helper(Container& c, Predicate p, associative_tag) {

               typename Container::iterator i    = c.begin()

                while ( i != c.end() ) {
                    if ( p( *i ) ) {
                        c.erase( i++ );
                    } else {
                        ++i;
                    }
                }
            }
        }

  • , STL wrote

    > anyway, what will the next video be about?

    I'm open to suggestions.

     

    How about SFINAE? This code is giving me unexpected results in Visual C++:

    template <typename T>
    struct is_container
    {
        template <typename U, typename U::const_iterator (U::*)() const,
                              typename U::const_iterator (U::*)() const>
        struct sfinae {};
    
        template <typename U> static char test(sfinae<U, &U::begin, &U::end>*);
        template <typename U> static long test(...);
    
        enum { value = (1 == sizeof test<T>(0)) };
    };
    
    #include <iostream>
    #include <vector>
    #include <list>
    #include <set>
    #include <map>
    
    int main()
    {
        std::cout << is_container<std::vector<std::string> >::value << ' ';
        std::cout << is_container<std::list<std::string> >::value << ' ';
        std::cout << is_container<std::set<std::string> >::value << ' ';
        std::cout << is_container<std::map<std::string, std::string> >::value << '\n';
    }

    The output on VS2008 is 1 1 0 0, but GNU gives 1 1 1 1. Is this a compiler bug?

  • @NotFredSafe:

    This is __not__ a compiler bug in either GNU/g++ or Visual Studio.

    Your code assumes that type U directly has member functions begin() const and end() const, vice inheriting them.  Your code also assumes that const_iterator comes directly from U, vice inheriting it.  The C++ standard does __not__ assume this for associative containers like std::map and std::set (see section 23.6.1 point 2 [for std::map] and section 23.6.3 point 2 [for std::set] of the current draft of the C++ standard).  According to the C++ standard, implementers are free to typedef std::map<...>::const_iterator or std::set<...>::const_iterator any way they want to ... so since Dinkumware uses inheritance to implement std::map and std::set, then Dinkumware can choose to just use the inherited std::_Tree<...>::const_iterator std::_Tree<...>::begin() const and std::_Tree<...>::const_iterator std::_Tree<...>::end () const.  Visual Sudio uses Dinkumware's STL implementation ... while GNU/g++ does not.  Dinkumware's std::map and std::set implementation uses inheritance ... while GNU/g++ does not.  If GNU/g++ used this inherited structure, then the results from GNU/g++ and Visual Studio would be the same.  Here's an example:

    #include <iostream>
    #include <vector>
    #include <list>
    #include <set>
    #include <map>
    
    template <typename T>
    struct is_container {
        template <
            typename U,
            typename U::const_iterator (U::*)() const,
            typename U::const_iterator (U::*)() const
        >
        struct sfinae {};
        
        template < typename U >
        static char test ( sfinae< U, &U::begin, &U::end >* );
    
        template < typename U >
        static long test ( ... );
        
        enum {
            value = ( sizeof( test< T >( 0 ) ) == 1 )
        };
    };
    
    template < typename T >
    struct ContainerInheritedFrom {
        typedef T const * const_iterator;
        const_iterator begin() const {
            return const_iterator();
        }
        const_iterator end() const {
            return const_iterator();
        }
    };
    
    template < typename T >
    struct ContainerThatInherits : public ContainerInheritedFrom< T > {
        //
    };
    
    int main () {
        std::cout << is_container<std::vector<std::string> >::value << ' ';
        std::cout << is_container<std::list<std::string> >::value << ' ';
        std::cout << is_container<std::set<std::string> >::value << ' ';
        std::cout << is_container<std::map<std::string, std::string> >::value << '\n';
    
        
        ContainerInheritedFrom< std::string > cif;
        ContainerInheritedFrom< std::string >::const_iterator cif_ci = cif.begin();
        cif_ci = cif.end();
    
        ContainerThatInherits< std::string > cti;
        ContainerThatInherits< std::string >::const_iterator cti_ci = cti.begin();
        cti_ci = cti.end();
    
        std::cout << is_container< ContainerInheritedFrom< std::string > >::value << ' ';
        std::cout << is_container< ContainerThatInherits< std::string > >::value << '\n';
    } 
    
    GNU/g++ Version 4.5.0:
    g++ main.cpp -W -Wall -o main.exe
    ./main.exe
    1 1 1 1
    1 0
    Visual Studio 2010:
    1 1 0 0
    1 0

    Because your code requires something that the C++ standard does not require, your code is not a good way to check for a container at compile time ... and is implementation dependent.  You should change your code to reflect the C++ standard requirements.

    Also, good info on Substitution Failure Is Not An Error (SFINAE) can be found on page 106 of C++ Templates: The Complete Guide by Vandevoorde and Josuttis.

    Hope This Clarifies,
    Joshua Burkholder

     P.S. - If you wanted to keep your previous structure and still be able to pick up the associative containers, std::set and std::map, then you could always use a partial specialization on your is_container<...> struct.  Here's an example:

    #include <iostream>
    #include <vector>
    #include <list>
    #include <set>
    #include <map>
    
    template < typename T1, typename T2 >
    struct is_same {
        static bool const value = false;
    };
    
    template < typename T1 >
    struct is_same < T1, T1 > {
        static bool const value = true;
    };
    
    template <typename T>
    struct is_container {
    
        template <
            typename U,
            typename U::const_iterator (U::*)() const,
            typename U::const_iterator (U::*)() const
        >
        struct sfinae {};
        
        template < typename U >
        static char test ( sfinae< U, &U::begin, &U::end >* );
        
        template < typename U >
        static long test ( ... );
        
        static bool const value = ( sizeof( test< T >( 0 ) ) == 1 );
    };
    
    template <
        template < typename, typename, typename > class S, /* Set */
        typename K, /* Key */
        typename C, /* Compare */
        typename A  /* Allocator */
    >
    struct is_container < S< K, C, A > > {
        static bool const value = (
            is_same< S< K, C, A >, std::set< K, C, A > >::value
            ||
            is_same< S< K, C, A >, std::multiset< K, C, A > >::value
        );
    };
    
    template <
        template < typename, typename, typename, typename > class M, /* Map */
        typename K, /* Key */
        typename T, /* T */
        typename C, /* Compare */
        typename A  /* Allocator */
    >
    struct is_container < M< K, T, C, A > > {
        static bool const value = (
            is_same< M< K, T, C, A >, std::map< K, T, C, A > >::value
            ||
            is_same< M< K, T, C, A >, std::multimap< K, T, C, A > >::value
        );
    };
    
    template < typename T >
    struct ContainerInheritedFrom {
        typedef T const * const_iterator;
        const_iterator begin() const {
            return const_iterator();
        }
        const_iterator end() const {
            return const_iterator();
        }
        
    };
    
    template < typename T >
    struct ContainerThatInherits : public ContainerInheritedFrom< T > {
        //
    };
    
    int main () {
        std::cout << is_container<std::vector<std::string> >::value << ' ';
        std::cout << is_container<std::list<std::string> >::value << ' ';
        std::cout << is_container<std::set<std::string> >::value << ' ';
        std::cout << is_container<std::map<std::string, std::string> >::value << '\n';
    
        
        ContainerInheritedFrom< std::string > cif;
        ContainerInheritedFrom< std::string >::const_iterator cif_ci = cif.begin();
        cif_ci = cif.end();
    
        ContainerThatInherits< std::string > cti;
        ContainerThatInherits< std::string >::const_iterator cti_ci = cti.begin();
        cti_ci = cti.end();
    
        std::cout << is_container< ContainerInheritedFrom< std::string > >::value << ' ';
        std::cout << is_container< ContainerThatInherits< std::string > >::value << '\n';
        
    } 
    
    Of course, this is a kludge (so you don't have to dramatically modify your code) and is not recommended ... but it works. Wink

    Based on the kludge above, GNU/g++ Version 4.5.0 outputs:

    g++ main.cpp -W -Wall -o main.exe
    ./main.exe
    1 1 1 1
    1 0
    Based on the kludge above, Visual Studio 2010 outputs:
    1 1 1 1
    1 0

     

  • ryanerryaner

    [quote]
    6 days ago, STL wrote
     
    However, it could be std::move(lvalue), in which case it might survive for a long time until reassignment or destruction.
    [/quote]
    Hi STL,
    I'd think in the case of move assignment operator, std::move will never be called with lvalue, correct? Because the move assignment operator takes an rvalue reference, and rvalue reference cannot bind to lvalue reference.
    But the explanation for the subtle differences are very valid, thank you for explaining that.
    Thanks for the awesome videos!

  • ryanerryaner

    [quote]
    a moment ago
    [quote] 6 days ago, STL wrote   However, it could be std::move(lvalue), in which case it might survive for a long time until reassignment or destruction. [/quote] Hi STL, I'd think in the case of move assignment operator, std::move will never be called with lvalue, correct? Because the move assignment operator takes an rvalue reference, and rvalue reference cannot bind to lvalue reference. But the explanation for the subtle differences are very valid, thank you for explaining that. Thanks for the awesome videos!
    [/quote]
     
    Ah scratch that question! I understan now. Somebody could write assignment operator with std::move(lhs) in which case it will invoke move assignment operator and resources will be swapped to an lvalue..

  • STLSTL

    [Deraynger]
    > Will this work?
    > typename Container::iterator

    Yep.

    NotFredSafe/Burkholder: Please note that it is technically forbidden to take the address of a Standard Library member function. (They can be overloaded, making &foo::bar ambiguous, and they can have additional default arguments, defeating attempts to disambiguate via static_cast.)

  • @STL:Thanks for the heads up on not taking the address of Standard Library member functions.  Where is that detailed in the standard?

    Thanks In Advance,
    Joshua Burkholder

  • STLSTL

    This is one of those things that the Standard doesn't explicitly say, but that logically follows from other Standardese.  (This is the second most subtle way that the Standard specifies stuff.  The MOST subtle way is "X is permitted because nothing prohibits it", which for example applies to v.push_back(v[0]).)

    From my BoostCon 2009 presentation:

    * What's wrong with mem_fn(&string::size) ?
    * It's nonconformant to take the address of any Standard Library non-virtual member function
    * C++03 17.4.4.4/2 permits additional overloads, making the address-of operator ambiguous
    * static_cast can't disambiguate: C++03 17.4.4.4/2 also permits additional arguments with default values, which change the signature
    * Lambdas for the win!
        * [](const string& s) { return s.size(); }

  • SecarSecar

    @STL:Are there any new videos planned yet ? I'm getting withdrawal symptoms!
     

  • STLSTL

    I'm filming Part 10, which will be the final part of this introductory series, on Thursday.  They're renovating the studio this month, but we found a temporary location where I'll be able to have a screen but no whiteboard.

  • @STL:Can you share the subject of this talk? Just can't wait, sorry Wink

     

  • Stephan,

    Great job with part 9.  This is the best episode of the series so far.  Thanks again for doing these.

    I'm sure you already have ep 10 all planned out, but I would really like to see an episode with perfect forwarding.  I'd like to see the type traits stuff too ... maybe that is in the next episode.

    Does the "final part of this introductory series" maybe imply that there would be an advanced series coming???

     

  • @STL:

    I hope "final" doesn't mean no more videos.
    Do you have any thoughts about what videos will come next ?

  • STLSTL

    [JeanGa]
    > Can you share the subject of this talk?

    Type traits, with some coverage of templates in the Core Language (not much) and some philosophy of templates (not much).

    [ryanb]
    > Great job with part 9. This is the best episode of the series so far.

    Thanks. What exactly do you like about it the most? The fact that it's covering a newer and more advanced topic?

    > Thanks again for doing these.

    You're welcome, and thanks for watching.

    > I'm sure you already have ep 10 all planned out,

    It's more like I have an idea, and I wing it as I go. The only parts I extensively prepared for were the Nurikabe segments.

    > but I would really like to see an episode with perfect forwarding.

    I expect that I will get to that in the advanced series. It'll help to see "live" examples of perfect forwarding.

    > Does the "final part of this introductory series" maybe imply that there would be an advanced series coming???
    ...and...
    [Mr Crash]
    > Do you have any thoughts about what videos will come next ?

    In the advanced series I'll dive into the STL's implementation. There are many neat tricks and subtleties in there, and it'll give me a chance to talk about things that can't be easily found in books.

  • SauliusZSauliusZ

    Hello,

    I hope that someone could provide me with elegant solution for this problem.

    Let's say we have vector with dynamically allocated objects, and we want to use remove_if to delete not necessary objects.
    I wrote such template (code below), and as You figured, I learned the hard way that you cannot use iterator returned from remove_if as begin or iterate it :)
    So one solution that I though about, is that predicate should delete those elements that should be removed, but this is to easy to forget.
    All other toughs ended up creating second vector or iterating vector 2 times...
    So maybe You could provide some elegant solution for this problem.
    Thanks.

    template<class TYPE, class PRED>void destroy_if(std::vector<TYPE*>& v, PRED pr){ std::vector<TYPE*>::iterator it, itb = std::remove_if(v.begin(), v.end(), pr); for (it = itb; it != v.end(); ++it) delete (*it); v.erase(itb, v.end());}
     

  • @SauliusZ:

    Did you look at part 3, STL's STL code Wink ? http://channel9.msdn.com/Shows/Going+Deep/C9-Lectures-Stephan-T-Lavavej-Standard-Template-Library-STL-3-of-n

    Unless I misunderstood something in the code, or your question, this should work for a vector (else look at STL's example to create a generic way of deleting container items):

    template <typename Container, typename Predicate>
    void erase_if_helper(Container& c, Predicate p) { 
        c.erase(std::remove_if(c.begin(), c.end(), p), c.end());
    }

  • SauliusZSauliusZ

    @Deraynger
    Well You see this example works grate if this container contains objects from stack, but if in container are objects on the heap (allocated with NEW) then this will leave memory leaks.
    One solution as I said would be to delete object in the predicate if condition is met.
    But I think that predicate should only determine if object should be removed, and NOT delete them. (well maybe this thinking is completely wrong and I should delete objects with predicate?).
    Or maybe there is some solution which wouldn't need to iterate container 2 times (one time to delete objects, second time to remove them from container).
     

  • , STL wrote

    Thanks. What exactly do you like about it the most? The fact that it's covering a newer and more advanced topic?

    Short answer, yes.

    The episodes I personally find most interesting are the ones that have taken a deeper dive into the library and/or have helped to introduce some of the newer (C++0x) aspects of the language.

    In this episode in particular, I found that it:

    - Introduced a feature (r-value references) that was mostly new to me

    - Showed what they really do at a low level

    - Showed me why I should care about them and how I can use them to solve problems in real code

    - Gave clear examples of where and how to use them

    - Let me see how they are applied within the library (and thus providing benefits) even if I am not directly using them in my code (improvements with only a recompile).

    - Presented a more advanced topic that you don't find in the piles of beginner STL info out there.

    And the most important part is that you have (as always) managed to present all of this using a very clear, uncomplicated, straight-to-the-point style.  You really do have a knack for understandable presentation of oft-complex material.  (Maybe you should write a book?)

     

    Anyway, your description of episode 10 sounds interesting.  I eagerly await that episode, as well as the advanced series.  It sounds like there is even more goodness coming there.

     

  • So maybe You could provide some elegant solution for this problem.

    Does the following code solve your problem?

    #include <algorithm>
    #include <vector>
    
    struct delete_functor
    {
        template <typename T>
        void operator()(T* p)
        {
            delete p;
        }
    };
    
    template <typename T, typename Pred>
    void delete_if(std::vector<T*>& vec, Pred pred)
    {
        typename std::vector<T*>::iterator middle = std::partition(vec.begin(), vec.end(), pred);
        std::for_each(middle, vec.end(), delete_functor());
        vec.erase(middle, vec.end());
    }
    

    But you are probably better off with a std::vector<std::unique_ptr<T>> if you have a C++0x compiler.

  • @STL: Excellent, i can barely wait

    "Must have C++0x / STL goodness, arrrrrg" Big Smile

  • STLSTL

    [SauliusZ]
    > Let's say we have vector with dynamically allocated objects

    As I believe I stressed in Part 3, every owning raw pointer (a T * that must be deleted) is a potential memory leak. Containers of owning raw pointers (e.g. vector<T *> where the pointers must be deleted) are basically guaranteed leaktrocity.

    You should use either vector<shared_ptr<T>> or vector<unique_ptr<T>>. You should be using VC10, so both should be available to you. If you're using VC9 SP1 (old!) you'll have access to shared_ptr (in std::tr1) but not unique_ptr. If you're using VC8 SP1 (argh, REALLY OLD!), you can use boost::shared_ptr.

    [ryanb]
    > The episodes I personally find most interesting are the ones that have taken a deeper dive into the library and/or have helped to introduce some of the newer (C++0x) aspects of the language.

    Thanks for the explanation; this will help me to target future episodes more precisely.

    > And the most important part is that you have (as always) managed to present all of this using a very clear, uncomplicated, straight-to-the-point style.
    > You really do have a knack for understandable presentation of oft-complex material.

    :->

    My ultimate goal for this series is to demonstrate that anyone can do this stuff if they approach it in the right way. It would be especially awesome to start a chain reaction - if I've helped you to understand something better, whether it's the STL or rvalue references or whatever, it's now your responsibility to spread this to other programmers and the codebases that you work on!

    > (Maybe you should write a book?)

    That's one of the things that I'd like to do. My understanding of C++ is still expanding, though. The more I learn, the more I realize how much else there is to learn.

    > Anyway, your description of episode 10 sounds interesting.

    I filmed it yesterday and it should be available in January. I spent a fair amount of time explaining SFINAE, which is a tricky Core Language concept.

    > as well as the advanced series. It sounds like there is even more goodness coming there.

    In the intro series, I assumed knowledge of C++ but not the STL's interface. In the advanced series, I'll assume knowledge of the STL's interface but not its implementation. I've spent a lot of time covering the basic concepts that I live and breathe all day (what's a vector, what's a shared_ptr, etc.). Now I'd like to try covering the most complicated stuff that I work with.

  • SauliusZSauliusZ

    @NotFredSafe
    Thanks, that will do it :)

  • , STL wrote

    > Anyway, your description of episode 10 sounds interesting.

    I filmed it yesterday and it should be available in January.

     

    And by "in January", you mean right on time for Christmas, right? Right??? I need my STL fix under the christmas tree Wink Can't wait for your SFINAE wisdom and other advanced insights... Perplexed

     

  • CharlesCharles Welcome Change

    The last installment in this series will ship before the end of December. Don't worry!

    C

  • Hi Stephan,

    This penultimate lecture of the fantastic introductory series is excellent and I'm looking forward to your coverage of <type_traits>.  You're a natural at getting across the salient points of your chosen topic.

    I'm impressed that you "wing it" in most lectures when you often elaborate on the reasoning and history behind features and decisions with such clarity, this is rare.

    Please keep recording lectures for as long as you enjoy doing so, I particularly look forward to an advanced series.

    Topics I'd like to see you cover in future lectures include:

    • The next two years for C++
    • The Boost libraries
    • Template meta programming
    • Algorithms and Big-O notation
    • Stephan's guide to the C++ specification and community
    • Your thoughts on concurrency with C++
    • Intermediate to advanced C++ advice
    • Existing C++ features and design patterns
    • Compiler features, settings and design, debugging tips
    • [EDIT] perfect forwarding, reference collapsing and especially noexcept [/EDIT]

    I'd like to be able to download the code shown in this episode, commented code would be a plus.

    Keep up the great work, thanks,

    Peter

  • STLSTL

    peteo: Thanks! It's a fair amount of work, but comments like yours help to keep me going. :->

    > The next two years for C++

    C++0x will officially become an International Standard, and compilers/libraries will increasingly support it. I can't show off what's coming in VC11, but I can walk through what we did in VC10.

    > The Boost libraries

    An episode about Boost is a good idea, and well-suited to the advanced series. One of the few downsides to my job, other than the fact that I get free Diet Mountain Dew but not free BBQ Popchips from the vending machines, is that I get very little time to use Boost. Other teams at MS use Boost, but the STL is the layer underneath Boost. Most of my Boost experience comes from college or from programming at home, so it's not exactly comprehensive. Still, things like Bimap, Filesystem, and Format (to name some things I've used and that aren't in C++0x) would be great to demonstrate.

    > Template meta programming

    Oh yes, you'll be seeing a lot of this.

    > Algorithms and Big-O notation

    I expect that I'll be going over some of this with the implementations of STL algorithms, although I've already given my Big-O spiel in the intro series.

    > Stephan's guide to the C++ specification and community

    The Working Paper is freely available (and the International Standard in PDF form is cheap). The Standard Library section is *mostly* intelligible to programmers who don't read Standardese all day, with the exception of iostreams/locales. As for the C++ community, there are probably a bunch of good sites I don't know about (and newsgroups; comp.lang.c++[.moderated] still seems to be active but I don't monitor it). Certainly I would say that Boost is the leading edge of C++ development.

    > Your thoughts on concurrency with C++

    This is a complicated topic and I'm not an expert here, but my PPL talk from BoostCon 2009 may be of interest to you: http://www.boostcon.com/site-media/var/sphene/sphwiki/attachment/2009/05/01/ppl.pdf

    > Intermediate to advanced C++ advice
    > Existing C++ features and design patterns

    This should come up as I'm looking through the STL's implementation; there are some good practices that we follow, places where we do stuff that ordinary users shouldn't do at all (like _Ugly identifiers) or should think carefully before doing (like some of the stuff we do with inheritance), and very few places where we do bad stuff that nobody should imitate and that we should fix (the most notorious example is using std::forward where we should use std::move).

    > Compiler features, settings and design, debugging tips

    Interestingly, I characterize myself as being very good at static code analysis and compile-time debugging, but lacking magic powers for run-time debugging. I try to, and usually succeed at, writing bulletproof code that works on the first try and doesn't need to be debugged. Conveying how to do this is kind of hard but I keep trying (and trying, and trying, even when people don't listen when I say that owning raw pointers are memory leaks waiting to happen, see above - my code is basically utterly immune to memory leaks because of how it is structured).

    > I'd like to be able to download the code shown in this episode, commented code would be a plus.

    I'm 99% sure that this is on my work laptop (I distinctly remember cleaning out old files, noticing my rvalue references examples on my desktop, and deciding to keep them because someone might ask for them). Unfortunately I'm on vacation right now, and my work laptop is the one thing that I don't have remote access to. I've written myself a todo to dig up this code when I get back after the new year. It was cleaned up and updated from my rvalue references v1 blog post.

  • STLSTL

    Part 10: http://channel9.msdn.com/Shows/Going+Deep/C9-Lectures-Stephan-T-Lavavej-Standard-Template-Library-STL-10-of-10

  • STLSTL

    Here's the code:

    #include <algorithm>
    #include <iostream>
    #include <ostream>
    #include <utility>
    using namespace std;
    
    class remote_integer {
    public:
        remote_integer() {
            m_p = nullptr;
        }
    
        explicit remote_integer(const int n) {
            m_p = new int(n);
        }
    
        remote_integer(const remote_integer& other) {
            if (other.m_p) {
                m_p = new int(*other.m_p);
            } else {
                m_p = nullptr;
            }
        }
    
        remote_integer(remote_integer&& other) {
            m_p = other.m_p;
            other.m_p = nullptr;
        }
    
    
        remote_integer& operator=(const remote_integer& other) {
            remote_integer(other).swap(*this);
            return *this;
        }
    
        remote_integer& operator=(remote_integer&& other) {
            remote_integer(move(other)).swap(*this);
            return *this;
        }
    
    
    #if 0
        remote_integer& operator=(remote_integer other) {
            other.swap(*this);
            return *this;
        }
    #endif
    
    
        ~remote_integer() {
            delete m_p;
        }
    
        int get() const {
            return m_p ? *m_p : 0;
        }
    
        void swap(remote_integer& other) {
            std::swap(m_p, other.m_p);
        }
    
    private:
        int * m_p;
    };
    
    remote_integer square(const remote_integer& r) {
        const int i = r.get();
    
        return remote_integer(i * i);
    }
    
    
    class remote_point {
    public:
        remote_point(const int x_arg, const int y_arg)
            : m_x(x_arg), m_y(y_arg) { }
    
        remote_point(remote_point&& other)
            : m_x(move(other.m_x)),
              m_y(move(other.m_y)) { }
    
        remote_point& operator=(remote_point&& other) {
            m_x = move(other.m_x);
            m_y = move(other.m_y);
            return *this;
        }
    
        int x() const { return m_x.get(); }
        int y() const { return m_y.get(); }
    
    private:
        remote_integer m_x;
        remote_integer m_y;
    };
    
    remote_point five_by_five() {
        return remote_point(5, 5);
    }
    
    remote_point taxicab() {
        remote_point ret(729, 1000);
    
        return ret;
    }
    
    
    int main() {
        remote_integer a(8);
    
        cout << a.get() << endl;
    
        remote_integer b(10);
    
        cout << b.get() << endl;
    
        b = square(a);
    
        cout << b.get() << endl;
    
    
        remote_point p = five_by_five();
    
        cout << "(" << p.x() << ", " << p.y() << ")" << endl;
    
        p = taxicab();
    
        cout << "(" << p.x() << ", " << p.y() << ")" << endl;
    }
    

  • Michael KilburnMichael Kilburn

    I was always cautious about rvalues even since they've appeared few years ago in discussions. And this video demonstrates that very clearly to a common developer -- rvalue adds a LOT of complexity to the language while falling short of achieving true move semantics. It is a great improvement, there is no doubt about it. But see for yourself:
    - using move constructor (ctor) still results in calling temporary's destructor (dtor). No one needs that thing anymore, why wasting CPU cycles to check moved values for some magical NULL values? What if type has no such values? In reality all you need is reduced mctor (no need to touch temporary object) and "don't call original temporary's dtor" guarantee provided by environment.
    - proposed move operator (mop=) v1 implemented as (MyClass(move(ref)).swap(*this)), it's price is: mctor + swap + 2 dtors! But all you need is release all resources you have (dtor call) + the same "reduced mctor" + "forget temporary" guarantee. And btw -- "swap" is worse than "move", "move" is worse than "reduced mctor".
    - mop= v2 (i.e. universal op=) is the same
    My problem with rvalues is that they are so complex and give so much advantage over what we have in language now that at the end of the day no one will ever be able to add more efficient true move semantic to C++ -- there will be only few interested people and it will very likely contradict with complex clauses in standard created to support rvalues -- you'll have to remove it... but then it'll break existing code.
    So, in my opinion rvalues are a partial solution that will make sure that we never get a complete one. :-(

  • Michael KilburnMichael Kilburn

    Sorry guys, when I was pressing "Comment" button formatting looked fine. Now it's not :-\

  • maikmaik

    Its a great video about rvalues, thank you!

  • STLSTL

    [Michael Kilburn]
    > using move constructor (ctor) still results in calling temporary's destructor (dtor).

    That's just a few instructions, and the compiler is much more likely to be able to see through them (it can't see through memory allocation/deallocation, but pointer assignments and tests are much friendlier to the optimizer).

    > What if type has no such values?

    Movable objects typically have resourceless states. Some objects don't, but that can be dealt with.

  • Michael KilburnMichael Kilburn

    > That's just a few instructions
     
    No, it is not. It is a function call (lets leave optimizer aside for a moment -- none of it's magic is guaranteed to happen anyway). And more -- move ctor has to be more expensive, move assignment -- even worse... C++ is usually quite proud of "not doing unnecessary work", but it is not the case with rvalues.

Remove this comment

Remove this thread

close

Comments Closed

Comments have been closed since this content was published more than 30 days ago, but if you'd like to continue the conversation, please create a new thread in our Forums,
or Contact Us and let us know.