File size

File size

File size

File size

File size

759.8 MB

In part 4, Stephan teaches us about Virtual Functions. In parts 1-3, we learned about compile-time constructs. Now, we enter the realm of runtime. STL spends some time discussing inheritance and a bit about access control.

I hope this makes C++ more intuitive and give it a new life. I often hear people saying C++ is dead but after listening to Herb Sutter's argument about the future of C++. C++11 is way to go. Keep up the good work. I hope C++11 have more support for weak memory models.

Too bad time ran out, because it would have been nice to show that the NVI idiom also applies to static polymorphism using the CRTP pattern with a class template `Base<Derived>`. Here you can explicitly downcast the this-pointer in the `Base` class and access a `Derived` class's private implementation, once again showing that access control is orthogonal to visibility.

Great stuff again, thanks. I find I use NVI a lot also although I never realised it had a common name. Usually this is because I want to make sure that a specific piece of code is always called before (or after) that part of the behaviour which is customisable by the derived class implementor. So a trivial example would be if I want to allow derived classes to customize a function, but I also want to ensure that the function's argument values are always logged to a file before calling the 'real' code.

The only annoying part of this pattern is that you have to come up with a slightly different function name for the virtual function... not a big fan of pre-pending 'do' although the other convention I've seen is *appending* 'Ex' which is also a bit rubbish!

C++11's <atomic>, which we shipped in VC11 RTM, fully supports weak memory models like ARM's. The atomics wisely default to sequential consistency, but if you want relaxed/acquire/release/etc. semantics, they are available.

Marek> What surprised me most about virtual functions is that pure virtual function can have implementation/body

Correct. "Pure" means "must be overridden" (if you want to get a concrete class).

yanshuai> Seems like a long time since the 3rd part.

There'll be another gap between this and Part 5 - I'll be on vacation for a couple weeks.

NotFredSafe> I would still like to see that "parameter passing best practices" episode

That definitely counts as part of the Core Language - the only thing I am avoiding in this series is directly covering the Standard Library.

I basically do episodes when I feel inspired enough about a topic to talk about it for 45+ minutes in a single take. Now that I'm basically done with "how a function call works", I can turn my attention to other matters.

> But if it's gotta be core C++, how about "sequence points are dead, long live the sequenced-before relationship"?

That is exceedingly subtle, and has almost no effect for single-threaded code. I figure that anyone experienced enough to make sense of the topic doesn't need me to explain it to them.

rhalbersma> Too bad time ran out

I'll never have enough time to cover every area of a topic (I could probably talk about the integral types for 3 solid hours), so my goal is to cover the basics and sketch out the structure of things so people are prepared to learn more, especially as they encounter issues similar to what I've talked about.

Philhippus> Is there a transcript and/or write-up of the lectures for reference purposes? Failing that how about time tags on the video?

Every episode is an undiluted stream of consciousness (mine is filled with cats meowing), but maybe Charles has some resources here.

GlassKey> The only annoying part of this pattern is that you have to come up with a slightly different function name for the virtual function...

The Standard conventionally uses a "do_" prefix. If I saw an "Ex" suffix, I would expect that (like in the Windows API) it referred to an extended form of an ordinary function (in C++ this is properly achieved by overloading, but C doesn't have that mechanism).

The override keyword is a nice solution for the case where I think I override but I don't.If I need to change the signature of the function in the base class, I wish I could rely on the compiler to give me an error for all the derived class that are now broken (which it will if override is used everywhere). However, I can't rely on that unless I know the override keyword is used everywhere...I wish we had a compiler warning when the override keyword is missing. Something we might get in Visual Studio someday?

Cool lecture, though vector<shared_ptr<>> made me go :/ - because of the performance of shared_ptr, esp if there are many of them. BTW Stephen IDK if you can comment but FB released folly(parts that I want to ask you about are optimizations of STL - does VS does something similar to stuff that folly does).To save your time I extracted cool STL features:1. (allocator here is malloc() if i get it correctly)"It doesn't take a rocket surgeon to figure out that an allocator- aware std::vector would be a marriage made in heaven: the vector could directly request blocks of "perfect" size from the allocator so there would be virtually no slack in the allocator. Also, the entire growth strategy could be adjusted to work perfectly with allocator's own block growth strategy."2. "In order to allow fast relocation without risk, fbvector uses a trait folly::IsRelocatable defined in "folly/Traits.h". By default, folly::IsRelocatable::value conservatively yields false. If you know that your type Widget is in fact relocatable, go right after Widget's definition and write this:

// at global namespace level namespace folly { struct IsRelocatable<Widget> : boost::true_type {}; }"https://github.com/facebook/folly/blob/master/folly/docs/FBVector.mdI guess you cant use 2. in STL, but you can use if for MS libs that use STL.

Regarding the memcyp trick I was wondering... is it possible using TMP to determine if a user class is memcypable(by making it memcpyable if every member is memcpyable, so class X{vector<string> vs; int y; } is memcpyable ).

Again great lecture, one cool thing would be auto. I know ppl think it is super simple, but actually it isnt. also you could reshow your trick question regarding for each and auto from GN conf. :D

That would for people who enable the warning, which means it'd be opt-in... I don't see that as a bad thing...

Those warnings are pretty mechanical to fix: assuming the code is otherwise correct all you have to do is add "override" on the line. It gets you to a state where a class of mistake can't happen:- adding a method to a derived class without realizing it already exists as virtual on the base class (so overriding it when it's not intended)- adding a virtual method on a base class when one of the derived classes already has a function with the same signature- changing the signature of a function on the base class and forgetting to update the derived class (this is what override is designed to fix, but how can I rely on it as the author of the base class if the keyword is optional? I could with a warning...)

@STL tnx for the answers...1)regarding vector and memcopyable ... what I wrote was wrong, I was thinking about swap and move. For example if I now call std::sort on vector whose elements are class X{vector<string> vs; int y; } isa) compiler smart enough to figure out that swap can be done by memcpya1) if not can programmer tell him that without manually writing sort function.

2) regarding auto, if you do it please mentionconst vector<double>& fun();auto x = fun;// x is not a const ref

One question about slicing: You suggest forbidding any assignment operator and copy constructor in the polymorphic base class. However, how would you in that situation implement the derived class's copy constructor and assignment operator?

I was always under the impression that slicing was precisely required to say Base(rhs) and Base::operator=(rhs); in the derived implementations.

@STL:"C++11's rvalue references v3 (not implemented in VC11 RTM) will automatically generate move ctors/assigns for X. Then swap() and sort() will automatically take advantage of them.".I had a really weird experience playing around with poor mans's v3 RVRs(memcpy) (VC11 RC)and hit really weird problems so I was wondering if you could explain it (for understanding of VS purposes, not to debug my code, like I said I did it for fun):http://pastebin.com/0NP1v0XLSo to recap code comments:1. in release mode memcpy specialized swap makes code a LOT faster. 2. in Debug mode with specialized swap I get runtime errors- if I was forced to guess: Debug runtime machinery doesnt deal ok with memcpy swap implementation. :D 3. in Debug mode by manually implementing Ctor error goes away. (this is really really weird).Ignore the lack of functionality of the code, it is just some random operations in ctor... to make element different from each other and to make debugging easier. Disclaimer : I reviewed my code to double check that it isnt some stupid mistake, but still it is possible that is the cause.

Awesome lecture, really gave me insight on the way to structure things.

I would be very interested in further discussion about avoiding inheritance for the sake of it. I see a lot of C++ developers that do "java C++" as I call it, with huge boilerplate classes hierarchies when something much more elegant could be conceived. I find it hard to find C++ resources that don't advocate this "inheritance based programming", and I'd love to hear your best practices about that.

Matt_PD> Regarding the NVI -- what are some good examples where we'd prefer "protected" access over "private" (and vice versa)?

You'd want "protected" when you'd want the base class to provide an implementation that derived classes can invoke as a helper. (The base implementation can be marked pure if you want to require derived classes to override it. Of course, if the base implementation is protected, pure, and implemented, a derived class can override it and just call the base implementation.)

> Another question -- is NVI the same as or different from the Template Method pattern?

I don't really pay attention to design patterns as I don't find them to be especially useful. I've heard that the NVI is one way to implement the "Template Method" pattern, which sounds right to me.

Ivan> I had a really weird experience playing around with poor mans's v3 RVRs(memcpy)

That's incorrect and dangerous. In the absence of rvalue references v3, you should write memberwise moves (with std::move).

In C++98/03/11 (the terminology has changed, but the principles have remained the same), only PODs (Plain Old Data) can be bit-blasted with memcpy(). Classes with copy constructors/etc. are "non-POD" and attempting to memcpy them triggers undefined behavior. std::vector is definitely non-POD and therefore you cannot memcpy it.

Chewie> I find it hard to find C++ resources that don't advocate this "inheritance based programming", and I'd love to hear your best practices about that.

See Effective C++, Third Edition by Scott Meyers. It has a whole chapter on inheritance, and an item (38, looking at the table of contents - my copy is at home) dedicated to when you should use composition instead of inheritance.

In general, I use inheritance when I need the one thing it does uniquely and well: runtime polymorphism. Otherwise, I'll reach for other C++ mechanisms first. For example, templates are better at compile-time polymorphism. What "compile-time polymorphism" means is when I want to write something that can work with many different types, but those types are all known at compile-time. The STL is the most obvious and famous example of this. vector<T> is a container of an arbitrary type T, but that type can be specified at compile-time. Making it a template allows it to handle these arbitrary types, without incurring any runtime overhead (everything is stamped out by the compiler and then inlined away, ideally).

Here's a wish for a future lecture, which doesn't fit into the current Core C++ series, but which I would like to get your insights on anyway. How about showing us your development setup and routine? So e.g. teach us about your VC editor/compiler/linker settings, any shortcuts/plugins/external libraries you often use, perhaps some advanced debugging tricks, precompiled headers, etc. etc. In short, anything that would increase productivity/build times for the rest of us.

I'm sorry if I'm too off-topic here, but I like C++ and there is something on my mind:The MSVC C++11 compliance is far too weak, and I have to admit that I expected more for VC11.

I love the things the standard committee guys did to the language, and I am trying to incorporate the new features into my way of thinking in C++ instead viewing them as extensions.That works really well with GCC or Clang, but when it comes to Visual Studio, I have this moment way too often where I think "WTF? Why does this $41T not compile?!!".

I'm sort of an open source guy and I'm all for supporting multiple platforms and compilers and I believe it's healthy to compile your code with several compilers, but I arrived at the point where I'm just about to drop support for MSVC in all my programs, regard it as broken and use MinGW for Windows compilation instead.

@STL:Are your core compiler collegues finally working on C++, or do they still have "other assignments"?Can you make a guesstimation of when the "in-between-release" of the compiler that Herb Sutter mentioned in GoingNative will arrive?

How do you find this situation? I could imagine you'd quite love to see more features to play with (variadic templates maybe?).

@gaya: if you recall episode 1 (name lookup), it should be clear that once the compiler finds operator% (even one with a different signature, which will be resolved during overload resolution) in the scope of class X, it stops looking for any other operator% in enclosing scopes. To call the free operator%, you can use ::operator%(a,1);

You'll see some of this in Part 6. My environment is very austere - I use a plain text editor and I drive the compiler from the command line. (When building test cases, I manually invoke "cl /EHsc /nologo /W4". When building the compiler and libraries, I use our internal build system on the command line. At home, I use makefiles on the command line.)

Alexander> Are your core compiler collegues finally working on C++, or do they still have "other assignments"?

The compiler team is working on C++11 Core Language features right now. In Part 6, I'll show off an internal build of the compiler (driven from the command line, since IDE integration is a separate chunk of work). I'd love to say which features are being implemented, but I'm not allowed to do that yet.

> Can you make a guesstimation of when the "in-between-release" of the compiler that Herb Sutter mentioned in GoingNative will arrive?

> How do you find this situation? I could imagine you'd quite love to see more features to play with (variadic templates maybe?).

I've often mentioned that as an STL maintainer, my work is greatly complicated by the lack of variadic templates. We've been imitating them with clever-but-horrible macro schemes since the VC9 Feature Pack.

gaya> if overloaded operators are just syntactic sugar

They are syntactic sugar for ordinary function calls, but they obey various special rules, especially for overload resolution.

rhalbersma is correct, but to explain further:

Given "a % 1", N3376 13.3.1.2 [over.match.oper]/3 explains that "For a unary operator @ with an operand of a type whose cv-unqualified version is T1, and for a binary operator @ with a left operand of a type whose cv-unqualified version is T1 and a right operand of a type whose cv-unqualified version is T2, three sets of candidate functions, designated member candidates, nonmember candidates and built-in candidates, are constructed as follows:". That's why both the member and non-member functions are considered by overload resolution (as a programmer would naturally expect).

Given "operator%(a, 1)" which is written as an ordinary function call, 3.4.1 [basic.lookup.unqual]/8 notices that you're in a member function body, so as soon as it finds X::operator% it stops. In general, C++ really likes calling member functions, which is why the unqualified name lookup rules are written like this. The Argument-Dependent Lookup rules also follow this preference - both unqualified lookup and ADL are performed, and both sets of declarations found are tossed into the overload resolution arena, *except* that if unqualified lookup finds a class member, the ADL set is emptied out (3.4.2 [basic.lookup.argdep]/3 has the full story and lists a couple of other conditions).

This is actually relevant in your scenario. Given the arguments (a, 1) the argument a is of type X which activates ADL. X lives in the global namespace, so ADL would search the global namespace and find operator%(X&, int) there - except that ADL has been disabled because unqualified lookup found a class member.

Note sure if this is the best place but I wanted to suggest std::function and related topics for a show. I have just started replacing a bunch of old style functor stuff with this and have run into a lack of documentation, for instance answering a question like "what does a std::function actually store and how does that relate to copying/assignment of std::function objects" seems hard to find out without reading C++ library code which is not the easiest code to read!

I'm programming a directory iterator and I'm getting a error message, but not from my program, from the filesystem header; saying " '_Ptr' : is not a member of 'std::tr2::sys::basic_directory_entry<_Path>'.

davidhunter22> I wanted to suggest std::function and related topics for a show.

I'll try to remember that for when I start doing videos on the STL again. If I forget, remind me. :->

> I have just started replacing a bunch of old style functor stuff with this

std::function is extremely useful, but it shouldn't be overused. For example, if you can template an algorithm on functor type, that is more efficient than having it take a std::function (which has inherent overheads).

> "what does a std::function actually store

Magic. But really, it just stores a copy of the functor you've given it.

> and how does that relate to copying/assignment of std::function objects"

The stored functor, including any state within, will be copied/assigned.

> seems hard to find out without reading C++ library code which is not the easiest code to read!

You shouldn't look at the guts of our implementation. If MSDN seems insufficient, you can look at the Working Paper for maximum accuracy.

Remove this comment

Remove this thread

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.