HomeОбразованиеRelated VideosMore From: CppCon

CppCon 2014: Herb Sutter "Back to the Basics! Essentials of Modern C++ Style"

1364 ratings | 171095 views
http://www.cppcon.org -- Presentation Slides, PDFs, Source Code and other presenter materials are available at: https://github.com/CppCon/CppCon2014 -- This talk revisits basic questions, such as how to declare and initialize a variable, how to pass a value to a function, how to write a simple loop, and how to use smart pointers, in the light of experience with C++11 and the latest C++14 refinements. This involves examining auto, rvalue references, range-for loops, uniform initialization, lambda expressions, unique_ptr and shared_ptr, and more. -- Herb Sutter - Author, chair of the ISO C++ committee, software architect at Microsoft. -- Videos Filmed & Edited by Bash Films: http://www.BashFilms.com
Html code for embedding videos on your blog
Text Comments (141)
Dingo Egret (20 days ago)
That Mike Acton guy needs to watch this
noxmetus (1 month ago)
The concept of clear simple code is not so clear. In one of my teams people didn't know range-for loop, it wasn't clear for them. “Clear” code for the developers in that team was the code that the least knowledgeable C++ developer could understand, practically C style. Otherwise they used to say it's difficult to follow. Also less function calls was more clear for them. A thousand lines of code in one function was better then 10 function calls, because they said otherwise they have to search and check each function code to follow. But what if the function name is very clear I used to argue? Doesn't matter. No amount of arguments you might hear from Herb, or Bob, or Kelvin, or Sean could help. They opposed even auto. Ah, and no templates. What I am saying is it might be quite challenging to serve that ~3M community of C++ developers. Sometimes I think we have to revert C++ back to C to make code “clear” for them. They program ---C++.
noxmetus (7 days ago)
They can understand it. It's just not what they consider as clean code. What I am trying to say is that if we try to serve 3M community of C++ developers, we must admit that we have to forget about clean code in Herb's sense. Most of these 3M developers program to implementation, not to interface, and don't want to learn anything new.
Meatboy 106 (7 days ago)
The main problem is that they are reluctant to accept change like many "experienced" guys. But any C++ programmer who cannot understant a range-for loop is dumb or (more likely) in bad faith.
Ihor Yalovetskyi (1 month ago)
So, why the fourth bar is so small in the last benchmark (the on the right)?
Meatboy 106 (7 days ago)
For the performance to be like that, you need your char* to be an rvalue in the first place. set_name("John"); //OK char* name = "John"; set_name(name) //Not OK Now, your temporary char* is passed to the templated set_name. The compiler then specializes the template with T = char*&& (rvalue reference). Then, the reference collapsing rules apply and make 'char* && &&' become 'char* &&'. However, the parameter name is an lvalue, just as any reference (rvalue or lvalue ref). Thus, it must be "converted back" to an rvalue before being sent to the assignment operator of string. This is what std::forward is made for. Finnaly, string::operator=(char*&&) is called. This causes the char* to be used by the string directly as its buffer (no copy, no assignment). In the end : 1 allocation to create the char*, no conversion, no copy. The other approaches involve at least one more allocation for the conversion from char* to string followed by deletions.
0x1A3C3E7 (1 month ago)
As much as I admire people who enjoy exploring how compilers work under the hood, I hate it. I prefer to focus on modeling applications from the top down based on requirements analysis and letting automation tools deal with building and optimizing the machine code from the bottom up based on my models.
cream bun (1 month ago)
LoL.
piggy66 (3 months ago)
that titus guy at 1:38:19 is a smug douchebag
Keith B (3 months ago)
@ 30:05 Can someone explain to me what it means to make a type _track_ or _stick_? Beyond deducing the type or explicitly declaring the type I do not understand what these terms mean, nor have I heard them outside of this talk. Thank you.
Bing Bang (15 days ago)
It just means that auto "tracks" the type itself. For example if you have to classes class Tool; class Hammer : Tool; If you now have an object of the type "Hammer" but you want it for any reason to be sure it should be stored as a Tool and not a hammer, you should stick it.
John Appleseed (3 months ago)
Did someone handpick that thumbnail clip? :P
Elite7555 (5 months ago)
Sorry, guys, but your questions are stupid, most of them aren't even questions.
JiveDadson (7 months ago)
Correction, Herbie. The type of "Hello" is not const char*. It's char[6]; 40:56
Peterolen (3 months ago)
Correction, the type of "Hello" is *_const_*_ char[6]._ But that doesn't change the fact that _auto s = "Hello";_ will deduce the type of _s_ to be _const char*._
JiveDadson (8 months ago)
There must be hundreds of boilerplate writers (obfuscation engineers) at Dinkumware.
Benjamin Reemts (10 months ago)
the content is great but the cut's are horrible, there are so many times the slides would be helpful but only the speaker is visable
F. KATS (11 months ago)
This is all very interesting, but it would also make sense IMHO to, at some point in the life of the C++ standardization process, spend some time to ensure that the C++ standard library is usable with Unicode text. I mean, usable as in Python 3, for instance.
Gerhard Biebl (1 year ago)
at 9:02 Herb Sutter encourages to prefer range loops over conventional counting loops. I cannot see any advantage in this whatsoever, if you don't count the additional array border safety. And i don't ! No developer in his right mind will produce an array border violation after 1 year of experience. Try this to judge for yourself : #include <iostream> using namespace std; #using <System.dll> using namespace System; const unsigned int Size = 60000; void main() { char Numbers[Size]; DateTime EndTime = DateTime::Now; DateTime StartTime = DateTime::Now; TimeSpan elapsed; // Counting Loop: for (unsigned int i = 0; i < Size; i++) // forward iteration //for (unsigned int i = Size-1; i > 0; i--) // backward iteration { Numbers[i] = i; } EndTime = DateTime::Now; elapsed = EndTime - StartTime; cout << "conventional counting for loop took " << elapsed.TotalMilliseconds << "ms" << endl; // Range loop: // First weakness: "for each" cannot work with pointers to arrays on the heap // Second weakness: The lack of a loop variable implies an external variable // in order to initialize big arrays, which have to be incremented separately // Third weakness: Only forward iteration is possible // Last: It is not faster than the conventional counting loop // Summary: Modern for loop has no advantage over conventional counting loop whatsoever. // At least regarding arrays, if you do not count array boundary violation safety // (and i do not) StartTime = DateTime::Now; unsigned int Counter = Size; for each ( char& ri in Numbers) { ri = --Counter; } EndTime = DateTime::Now; elapsed = EndTime - StartTime; cout << "modern for loop took " << elapsed.TotalMilliseconds << "ms" << endl; }
Meatboy 106 (7 days ago)
Can you please do the same with a structure without random access iterators such as a list ? Can you also give me one good reason to continue using c style arrays on the heap when std::vector has almost zero runtime overhead while providing everything you need for both forward and backward iteration ? And finally, why should I bother with a counter if everything I do is "for each element, do something, regardless of its actual index" ? The only reasons I can think of to use a counter is when you actually need the index and when you are iterating over several containers simultaneously.
Elite7555 (5 months ago)
Look, the "conventional" for-loop with this manual counter is just cumbersome. Most of us just wish to loop through an array in a comfortable way, like in Java or JavaScript. For your special cases, you can do your traditional for-loop, but it is not the rule.
Sedit T (7 months ago)
You do see the irony in bragging about why not to do something when at the same time making a mistake showing exactly why to use something...
Gerhard Biebl (9 months ago)
Thank you - must have happened when i inserted that line along with other alternative comments. Very astute ! Well done! But what really went wrong there was that accidentially (maybe because i was working on a project where i used this one) i chose the "wrong" implementation of the range loop - in fact, this code partially is "Managed C++" instead of C++11, which means i used a for each loop from .NET including the "System.dll". The proper loop header schould be: "for( char& ri : Numbers )" which is c++11 and not "for each (char& ri in Numbers)" which is managed c++. In MFC there also is a "for_each" range loop (Note the underscore). But that one would need iterators, which a primitive type char array does not have. This is the cause why they introduced an "Array" class in C++11 which exposes iterators even for primitive types.
Peterolen (9 months ago)
In your commented "backward iteration" code did you skip the first element of the array on purpose, or was that a mistake?
1:24:10 "...when you copy you also have to get rid of the previous state; when you move you don't" He mean't the exact oposite, right?
Devasted (25 days ago)
imagine a variable x and y. If you create variable x, it exists on memory. If you are copying x to y, x is still alive. But if you move it, you move the complete variable and x is no longer alive.
Devasted (25 days ago)
No, he is right
Cas Snel/informatie (1 year ago)
HAhahahahhaha
Rajeev Mehta (1 year ago)
At 1:17:00 setName function is marked as noexcept and takes an rvalue parameter and perfectly forwards it. But if setName is passed an lvalue, it might cause an allocation inside the function as it would be an assignment? In that case it cant be marked noexcept?
Meatboy 106 (7 days ago)
True. The expression inside the noexcept qualifier make the function noexcept if and only if the String type can be assigned to std::string without throwing. Otherwise it is not noexcpet qualified.
p rednaZ (1 year ago)
When it comes to option 4 for set_name, Herb, the confident guy in the audience and the guy expressing his surprise about its performance advantage in the end curiously all messed it up at some point. Herb firstly messed it up, because compilation of the code on his slide will fail as soon as used with a string argument because of the exclamation mark. Without it, compilation will still fail as soon as used with a char* argument, as during his benchmark. Herb is simply wrong with his "Write it right", instead do not constrain the template unless the function is overloaded. So the confident guy in the audience is right saying Herb constrained the template wrong. But it is even more wrong than he apparently expected. Because of the erroneous exclamation mark, the function will indeed accept a string literal. Finally the guy expressing his surprise about the performance advantage of option 4 appears to have a valid point at first sight because without the exclamation mark the template should generate at compile time the exact code of option two. But it only does so if used with a string argument. A char* argument causes compilation to fail as mentioned before. If the entire constraint had been correctly left out, then there would be no reason for surprise either because the generation of a char* template instantiation enabling the usage of string::operator=(const char*) clearly accounts for the performance advantage. Despite Herb's attempts to shift responsibility onto Howard, I think, Howard is the only one involved who has got everything right. He was dealing with an overloaded function in a different context like a constructor overloaded with copy and move constructors. His function being overloaded with a function taking some type X, in order to prevent the forwarding reference from matching X too, he had to write an template constraint to exclude X from the template, hence the exclamation mark. Herb just blindly copied Herb's code from a different context, fiddled with the data types according to his faulty understanding of the purpose of the constraint, and ended up with something which is wrong for multiple reasons.
MeMelster (1 year ago)
isn't there also a risk that auto will fuck up my shit? For example: double vec[2]={2, 5.3}; auto dummy=0; dummy=vec[1]; Now fummy will be just "5" because i missed to type "0.0" if i just had used double it would be no problem.
That has nothing to do with "auto" and everything with you doing an implicit type conversion at the "dummy=vec[1];" assignment.
slipcurve (1 year ago)
MeMelster that's not auto fucking up your shit, that's you fucking up your shit. type is deducted at the initialiser line, not later. auto dummy = vec[1];
HebaruSan (1 year ago)
Auto... what a debugging nightmare it would be if the types of objects were hidden like that.
piggy66 (3 months ago)
it's called type inference you dumb mother fucker, many many strongly typed languages have been doing it for decades. It's fine. Now shut the fuck up.
LongLostWraith (1 year ago)
Thankfully the types are visible to debugging tools. What people fear the most is having a type you didn't intend to have, IMO in that case do use explicit type or the auto syntax he showed.
M M (1 year ago)
Is there a cheat sheet for all this? (especially the unique_ptr and pass by ref vs pass by pointer etc)
Bastian Weber (1 year ago)
Can anyone explain me what the NOT-operator (!) is doing in the perfect forwarding example? From what I see the author wants to restrict the templatisation to the type std::string. I'm referring to this line: template<class String, class = std::enable_if_t<!std::is_same<std::decay_t<String>, std::string>::value>> I don't understand why the NOT operator is there. In my opinion it results exactly in the opposite of what shall be achieved.
p rednaZ (1 year ago)
Please refer to my other comment below this video.
Luftbubblan (1 year ago)
If i want to get my hands on gcc 7 where do i go?
Luftbubblan (1 year ago)
Nvm, look like i'm gonna try Clang instead. Seems to be a somewhat related version in my repo.
Gal Anonim (1 year ago)
It drives me crazy that so many of this video shows Herb instead of his code!
CobraL0rd (23 days ago)
Herb is the code!
LukeGlue (1 month ago)
MrSnowman  Yeah seriously. It is like they swapped to a fresh camera guy that wanted to get more action.
MrSnowman (8 months ago)
Yeah especially 56:00 and forward. He even draws attention specifically to the importance of watching the screen right then. Whoever put this together doesn't care. He's just mesmerized by Herb I guess.
Jerry Grauert (1 year ago)
https://github.com/CppCon/CppCon2014/tree/master/Presentations
Jacob Moen (1 year ago)
Go and grab the slides, and have Herb and his code at the same time! :)
kimga85 (1 year ago)
auto x = 10; Why not remove it all together and just write x = 10;
Peterolen (9 months ago)
I guess they could change the rules so that the variable is automatically declared if the variable name is not already declared in this scope but it won't happen because it would be terribly error prone. If you meant to create a variable but the name happens to be used for a different variable in an outer scope you would accidentally be modifiing this other variable. If you want to assign to a variable but misspelled the name you would not get an error message because it would just assume you wanted to create a new variable.
Marc Chapman (1 year ago)
Because that becomes an assignment statement not an initialiser statement
minciNashu (1 year ago)
40:07 "try sticking it to the right, it will feel weird the first couple of times" lol
Chris St (1 year ago)
Question for the slide "pass by value" (1:12:00) : Would the code work also without the std::move? Is it used only for performance?
Meatboy 106 (7 days ago)
Sure, it would work. However it would be slower for longer strings (short strings are actually copied and moved at the same speed). Long things short : without move -> allocation and memcpy ; with move -> copy of a pointer. When not using move, you are calling the copy assignment operator that 1. resize the buffer of the name_ string (allocation) 2. copy the content of the buffer of name into the buffer of name_ 3. delete the buffer of name at function return. When using move, you are calling the move assignment operator that 1. delete the current buffer of name_ 2. bind the buffer pointer of name_ to the buffer pointer of name 2. set the buffer pointer of name to nullptr (or equivalent)
Mike Vasiljevs (1 year ago)
This is quite a weird beast "template<..., class = ...>", I guess I am visiting too few TMP hackathons!
Mike Vasiljevs (1 year ago)
can somebody please explain why this is a good thing?
Mike MGarcia (1 year ago)
good way to sell books... add crap to C++
Fernando Moreira (4 months ago)
Jacob Moen Agreed, but nothing prevents a language from one kind becoming the other.
Jacob Moen (1 year ago)
“There are only two kinds of languages: the ones people complain about and the ones nobody uses.” ― Bjarne Stroustrup, The C++ Programming Language
TheEVEInspiration (2 years ago)
I had to shake my head a few times so far. Using smart-pointers is always messy, it promotes not thinking about the lifetime of objects at an architectural level. It creates multiple ways to do call chains that are fixed, while requirements might change at a deeper level. Better is to avoid the heap concept as much as possible, even if under the hood a heap is used, the stack model is the natural model for C++.
grisevg (1 year ago)
Watch his Cpp2016 talk "Leak-Freedom in C++" where he shows how to write simple, fast and leak-free code. And yes, he says that most of the time you should use stack and not heap.
teatime90 (1 year ago)
TheEVEInspiration c++ is being fitted for software factory purposes
Ashamaali (2 years ago)
" it promotes not thinking about the lifetime of objects at an architectural level" Exactly. They undoubtedly have their uses, but this point needs to be made.
dandymcgee (2 years ago)
Whether or not you agree with what he says, nobody can disagree that this guy is a brilliant public speaker.
darkgaze (1 year ago)
I don't agree. He's good in style, but his slides are TERRIBLE. He doesn't follow any recommendation on slide style I've read. He adds lots of text he doesn't read to the audience, who's trying to listen and read at the same time. He adds "taboo" colors, like light green, yellow and such. He uses different styles on each slide. Seriously, to be there in person, I'd never understand half of it, if you are not an expert.
WesOfX (2 years ago)
I agree with the philosophy here. The day I gave up on typing perfectly optimal code is the day I became 100x more productive
Banefane (2 years ago)
Well, allright lets make c++ more powerful even though I don´t believe that every change will be successfully adapted :)
A Wall (2 years ago)
By any measure, C++ is an elitist language and apparently driven to become more so. According to the latest statistics, there are about 20 million software developers worldwide, and C++ developers account for about 3 million - 15%. Little wonder; this has to be the most obtuse, visually offensive syntax of any language, ever. Really? - "for(auto& e : c) { use(e);}" - any programmer should be able to understand what that does. Or even more cryptic - "for(e : c) {use(e);}". Yep, that's going to get kids all over the world begging to learn C++. Hey kids, learn JavaScript. More jobs, more money. And an elegant, powerful language you will come to love.
Sascha Gary (26 days ago)
more money with JavaScript? JavaScript elegant? lmao
Tim Teatro (11 months ago)
I'll never understand people who think the world only needs one language. And coincidentally, it's always their own favourite language.
LongLostWraith (1 year ago)
lol, gtfo with your polarizing bs. We should use the right tool for the job, and sometimes that is C++.
Martin Acevedo (1 year ago)
JS is not a programming language, it is something that can be use to prototype obscure ideas.
Stuntddude (1 year ago)
C++ may be a terrible language, but Javascript is among the few that are even worse.
Rafael Nogueira (2 years ago)
I want so much to become a professional C++ programmer , i know simples things for now i do computer science . But where I live there is not any possibility of chance to get any job or oportunity...
fzort (1 year ago)
Stop making excuses. You can contribute to open source projects and gain experience working with some of the best programmers in the world. If you're good enough you can work remotely. I live in Brazil but work remotely with C++ for an European company.
Mariano (1 year ago)
Also, if you're going to be using a low-level language like C++ you should learn a little bit about computer architecture. Even if you never end up doing anything close to the hardware like embedded or drivers C++ exposes those low-level concepts to you and so it makes sense to learn a bit about them. A fun exercise may be writing an emulator, programming a microcontroller dev/evaluation board, or trying to implement a hardware protocol in code.
Rafael Nogueira (1 year ago)
Conenion i study graphs , binary trees , algorithm complexity , all kind of sorting algorithms and arrays structures ,pop push etc , every kind of things in the university I don't know everything but I can handle
Conenion (1 year ago)
To become a professional C++ programmer, you have to become a professional in algorithms and data structures first. Along with a lot of practical experience, not only in programming. Knowing a programming language well doesn't make you a professional.
Eve (2 years ago)
aww poor you, go cry some more
Frank Wang (2 years ago)
no subtitile?
Quentin UK (2 years ago)
"C++ is evolving man, we're going faster." 2
sradnz015 (3 years ago)
"ptr"
Quentin UK (3 years ago)
Hi with the Herbs.
Hanzhou Shi (3 years ago)
A lot of food for thought.
Tristan Wibberley (3 years ago)
Is there any room to change the spec for a simpler solution to the shared_ptr dereferencing problem at 23:48? Ideally, the library specification should have been that dereferencing operations on std::shared_ptr&lt;T&gt; should all return a std::shared_ptr_deref&lt;T&gt; object that ups the refcount during its lifetime and which has an implicit conversion to T&amp;. This way the reference will always be held while the object is being used. I hope that can be changed in upcoming c++ versions
Meatboy 106 (7 days ago)
What about the overhead of such solution ? Ref counting is thread safe in case you forgot, which makes it very expensive in a context of repeated pointer dereferencing. Here, you have to take care of yourself in these quite rare cornercases. Otherwise, it's as fast as it can get.
Tanzinul Islam (1 year ago)
you can just copy the shared_ptr to increment the refcount, no?
Dylan W (3 years ago)
mistitle
Dainius Figoras (3 years ago)
35:09 "why deduce correctness" IMO weak argument to go to auto, because if you change something so significant that you starting modifying, when you haven't before, it's not small change, and have need to look at code more careful (because now types are different) is just a plus.
lockbert99 (3 years ago)
Why is he saying "share putter" and not "shared pointer" ?
PS (4 months ago)
raw pointer ripens to either shared putter or unique putter....
Dale Willits (1 year ago)
He's just pronouncing ptr as "putter." Like he says "stud" for std.
lockbert99 (3 years ago)
I envy people who code in languages that don't have things like "r-value references" and have experts arguing about whether something should be called a "universal reference" or a "forward reference".
Mercede (3 years ago)
why is the camera mostly on him rather than the slides?
Siwen Zhang (3 years ago)
This is extremely useful, especially the parameter passing.
BitPuffin (3 years ago)
Why wouldn't you be able to use it? You just call "get" and get the raw pointer.
john doe (3 years ago)
THANKS for the upload!!!
10100rsn (3 years ago)
"This is the slide ..." @ 58m25s
von nobody (3 years ago)
Hi I'm Jon and I'm CA :D
Xiaolong Qian (3 years ago)
a small advice for the video maker, you need to focus the camera a little more on the slide.... we need time to read that.... thank you
qqi239z123 (3 years ago)
Addicted to complexity indeed: cannot have a talk without self-contradictions. Non-owning raw pointer means private destructor, private destructor, means make_unique, make_shared cannot be used. But a really nice try anyway.
Meatboy 106 (7 days ago)
He was mostly talking about function parameters. When you just use an object that belong to someone else, may it be on the stach, on the heap, a smart_ptr or anything. I can't see where you need a private destructor to do that.
vonkruel (3 years ago)
Quite worthwhile.
Ville Nurmi (3 years ago)
Perhaps he means that range-for doesn't let you write a break condition that depends on the iterator. You only see the values behind the iterator.
Vladimir Moolle (3 years ago)
What's wrong with the quality now?? This 720p looks like it is a poor 360 :)
Rui Figueira (3 years ago)
Like Herb mentioned: "It reflects bias to code against implementations, not interfaces". Good code design should focus on "what" the code does, and not "how" it does it. This is particularly important if you are creating libraries for other people to use.
John Rowe (3 years ago)
c2com interface the code 2015
David Rodríguez Ibeas (3 years ago)
No. The main difference is that the copy-and-swap idiom is not about avoiding unnecessary work, but about providing stricter guarantees, in particular regarding exception safety. If you want to have the strong exception safety guarantee, you must pay the price and create a new temporary object, for that you cannot leverage the memory that your current object holds.
xgihavoc (3 years ago)
This is a good point. In most projects I've worked on, the first one is by far the most common (maybe because it's older) but very easily recognizable as "oh, that's an STL object iterator!". The other thing to look at here is how readable it is to new developers of C++. In my opinion, it makes more sense and is more readable because you are saying: "I'll start the beginning, until I reach the end, and advance one object per iteration" pretty explicitly.
Meatboy 106 (7 days ago)
Does it make python an obfuscated language ?
xgihavoc (3 years ago)
Even during this talk, I feel Herb himself is a victim of what he is trying to teach against. The simple mention of "does this every time you are iterating through all elements" instead of "any time you iterate" means that you have to have two variations depending on whether you iterate through some or all. Isn't this "non-default" in itself?
Meatboy 106 (7 days ago)
It is default. More than 70% of my for loops are range-for loops. Other loops are either reverse iterations or index based iterations because I need the index value.
Soul Studios Music (3 years ago)
9:30 - answer is, the top one is easier to parse visually by someone coming to the code who hasn't written it. In fact it would be even clearer without the 'auto' as the type would be immediately visible.
Roberto Giménez (3 years ago)
1:15 the concept of "unteachable" perfectly illustrated
Dusten Sobotta (4 years ago)
The auto keyword is evaluated at compile-time.
Steve Robb (4 years ago)
If we're no longer encouraged to pass-by-value on setter functions, does this mean we should also discourage copy-and-swap assignment operators, which suffer from the same problems?
About prefering auto for declaring local variables: The auto keyword makes code harder to read (type deduction) or document. It makes it worse even, because if you change a return type you don't want undesirable side effects that pass undetected. The compile time error is your friend when refactoring a code base. Much better than having run-time erros and it also gives the opportunity to review the code and detect problems ahead of time.
LukeGlue (1 month ago)
You're kind of missing a big piece of what he is saying. If you're coding to an interface then you can use his "tracking" auto declaration but if you are coding to an implementation then you need to use his "stick" or "commit" style which is 'auto x = type { foo() };'. This is better b/c it conveys that your intention is to code to a specific type and not an interface. It is more expressive.
ooltimu (4 years ago)
What does he mean "range-for doesn't support early break" ? I see it does.
Tony DaSilva (4 years ago)
As usual, great stuff Herb!
Giumo Clanjor (4 years ago)
And, I found that it's easier to line up all those equal signs (tabularize them) with auto
Ezo man (4 years ago)
Thanks for this. It clarified many things that I wasn't sure how to do(mainly about rvalue).

Would you like to comment?

Join YouTube for a free account, or sign in if you are already a member.