> By noexcept we tell the function that no exception should be thrown, right?
We tell the caller of the function (and the compiler) that we do not expect this function to propagate an exception to its caller.
> For what functions/ctos etc do we need to use that keyword, please?
Move constructors and move assignment operators should almost always be having a non-throwing guarantee (Destructors too, but they are noexcept by default). For other functions, this is the rule that I follow: "If it is clear that the current and future implementations of this function should not throw, then I specify it as noexcept"
> On line 28 both temp and that have the same address and contents, right?
They have the same contents; temp is a copy of that. These are two different objects; they have different addresses.
> On line 32, if std::swap, swaps the contents of its parameters and won't throw, then why did we need a temp vector (line 28)?
The copy constructor of the temp Vector is the one that acquires the the resources needed for making a copy. If the acquisition of new resources by temp is successful, the swap logically exchanges the contents of our vector and temp; we now hold a copy of the Vector that and our old contents would be released by the destructor of the Vector temp.
More information:
https://en.wikibooks.org/wiki/More_C++_Idioms/Copy-and-swap