I'm new to C++ and am new to using RAII for deleting allocated memory. I wrote this code as a sample of what it would take to automatically allocate and later delete a character array. I know there's a string class out there, but thought I'd start with the older stuff first. Does this sample look correct? And is it the way others would create a string and delete it automatically?
#include <iostream>
using namespace std;
class StringGetter
{
private:
char* theString;
public:
StringGetter(char* name)
{
this->theString = new char[1024];
strcpy_s(this->theString, strlen(name) + 1, name);
}
char* GetString()
{
return this->theString;
}
~StringGetter()
{
delete[] this->theString;
}
};
int main()
{
char* response;
{
StringGetter getter("testing");
response = getter.GetString();
}
cin.get(); // pauses the console window
}
It looks like you get the main idea, but there's a couple things worth mentioning.
1) As #chris mentioned, you're forgetting your copy constructor, copy assignment operator, move constructor, and move assignment operator. Copy should be either manually written or disabled, move can be defaulted. (aka You've not followed the rule of 5)
2) Prefer to use std::unique_ptr to represent ownership. It's already done all of the hard work for you. By keeping your wrapped string in a std::unique_ptr the default versions of the copy/move special functions will preserve correctness (though you'll still want to implement the copy operations if you want them to be enabled).
Here's what this might look like:
class StringGetter {
public:
explicit StringGetter(char* name) {
strSize = strlen(name);
str = std::unique_ptr<char[]>(new char(strSize+1));
std::copy_n(name, strSize + 1, str.get());
}
StringGetter(const StringGetter& other) {
strSize = other.strSize;
str = std::unique_ptr<char[]>(new char(strSize+1));
std::copy_n(other.str.get(), strSize + 1, str.get());
}
StringGetter(StringGetter&& other) = default;
StringGetter& operator=(const StringGetter& rhs) {
auto temp = rhs;
swap(temp);
return *this;
}
StringGetter& operator=(StringGetter&& rhs) = default;
const char* getString() const noexcept {
return str.get();
}
void swap(StringGetter& other) {
std::swap(str, other.str);
std::swap(strSize, other.strSize);
}
private:
std::unique_ptr<char[]> str;
int strSize;
};
Miscellaneous items:
1) Note that std::unique_ptr handles the RAII. When I replace 'str' in the copy constructor, it deletes the old string automatically.
2) I size the dynamically allocated memory to match the input string. This prevents overflows/wasted memory.
3) The constructor is explicit. This prevents undesirable conversions. Rule of thumb is to use the explicit keyword on all single argument constructors.
4) I made getString constant so that you can still call it on const instances of the class.
5) I replaced the str-family copy function with std::copy_n. It's more general and can avoid certain pitfalls in obscure cases.
6) I used the copy-swap idiom in the copy assignment operator. This promotes code reuse/avoids duplication.
7) When C++14 comes out, replace the std::unique_ptr constructor call with std::make_unique for added exception-safety and to remove redundancy.
Here is an attempt at an RAII class that does something similar:
template<std::size_t N>
class StringGetter_N
{
private:
std::unique_ptr<char[]> buffer;
public:
StringGetter_N()
{
buffer.reset( new char[N] );
buffer.get()[0] = 0;
}
explicit StringGetter_N(char const* name)
{
buffer.reset( new char[N] );
strcpy_s(buffer.get(), N-1, name);
buffer.get()[N-1] = 0; // always null terminate
}
StringGetter_N( StringGetter_N&& other ) = default;
char* GetString()
{
return buffer.get();
}
};
class StringGetter : StringGetter_N<1024> {
explicit StringGetter( const char* name ):StringGetter_N<1024>(name) {}
StringGetter():StringGetter_N<1024>() {}
};
notice that I delegated the resource management to a smart pointer. Single responsibility principle means that the resource management object (like a smart pointer) can do just that, and if you want to write a class that represents a heap-allocated buffer of fixed size, you delegate that sub problem to the smart pointer, and just manage it in your class.
As it happens, std::unique_ptr properly implements
But really, it is usually much simpler to just use a std::vector, as you can usually determine how much space you need at run-time before needing a buffer to write to.
If you do implement your own RAII class, you should follow these rules:
Single argument constructors that are not copy/move constructors should usually be explicit.
If you implement a non-trivial destructor, you must (implement or block usage of) (copy and/or move constructor) and (copy and/or move operator=). This is known as the rule of 3 (or rule of 5 in C++11).
Your RAII class should do little except manage the resource. If you want to do something else, use an RAII class to manage the resource, then store it within another class that does the fancy extra work.
Related
MY Previous question:
How's the Copy & Swap idiom really supposed to work, seriously! My code fails
In the code below, I need the variable auto ptr to remain valid and the assertion to pass.
auto ptr = a.data();
Looks like this:
+--------------+
| a.local_data | --\
+--------------+ \ +-------------+
>--> | "Some data" |
+-----+ / +-------------+
| ptr | -----------/
+-----+
#include <iostream>
#include <cassert>
using namespace std;
class Data
{
private:
char* local_data;
int _size = 0;
inline int length(const char* str)
{
int n = 0;
while(str[++n] != '\0');
return n;
}
public:
Data() {
local_data = new char[_size];
}
Data(const char* cdata) : _size { length(cdata) }{
local_data = new char[_size];
std::copy(cdata, cdata + _size, local_data);
}
int size() const { return _size; }
const char* data() const { return local_data; }
void swap(Data& rhs) noexcept
{
std::swap(_size, rhs._size);
std::swap(local_data, rhs.local_data);
}
Data& operator=(const Data& data)
{
Data tmp(data);
swap(tmp);
return *this;
}
};
int main()
{
Data a("Some data");
auto ptr = a.data(); // Obtains a pointer to the original location
a = Data("New data");
assert(ptr == a.data()); // Fails
return 0;
}
EDIT: To GIVE some perspective, the following runs perfectly well with the Standard C++ String class.
#include <iostream>
#include <string>
#include <cassert>
int main()
{
std::string str("Hello");
auto ptr = str.data();
str = std::string("Bye!");
assert(ptr == str.data());
std::cin.get();
return 0;
}
And, I am trying to achieve the same functionality.
In terms of correctness, contrary to what some comments indicate, your assignment operator looks correct for copy/swap:
Data& operator=(const Data& data)
{
// Locally this code is fine
Data tmp(data);
swap(tmp);
return *this;
}
It makes a copy of the data into tmp, swaps with it. Thus, the current object's new state is a copy of the data, and the object's old state is inside tmp and should be cleaned up in its destructor. This is exception safe.
However, it depends on two key things that you failed to do (as the comments did point out in part):
a non-throwing destructor that cleans up the old state. You omitted this, and it is crucial for proper management of the resources this object owns.
~Data()
{
delete [] local_data;
}
note: you don't need to set it to nullptr, and don't need to check for nullptr, because deleting a null pointer is a noop, and once the destructor begins running, the object ceases to exist (lifetime is over) and so it should never be read again or your program has undefined behavior.
You did not write a copy constructor.
When you don't write a proper copy constructor, the compiler generates one for you that does an element-wise copy. That means you end up with a copy of the pointer, not a copy of the data to which it points! That is an aliasing bug because both objects will point to (and logically "own") the same memory. Whichever is destroyed first will delete the memory and corrupt the memory to which the other still points. Fortunately, a copy constructor is easy to make for your class:
Data(const Data& other) :
local_data{new char[other._size]}
_size{other._size},
{
std::copy(other.local_data, other.local_data + _size, local_data);
}
Things to observe about this copy constructor:
if new[] throws, nothing is leaked. copy() cannot throw. This is exception safe.
the order of initialization is not the order listed in the constructor, but the order the data members are declared in the class. Thus, local_data will be initialized before _size, and so it's important to use other._size for the new expression.
The copy/swap idiom is clean, and concise, and can lead to exception safe code. However, it does have some overhead, as it makes an extra object off to the side, and does the work to swap with it. The benefit of this idiom is when multiple operations can throw exceptions, and you want an "all or nothing" assignment. In your particular class, the only thing that can throw is the allocation of local_data in operator=, and so it is not really necessary to use this idiom in this class.
I think your code should be ok after adding these functions. In this case, you would benefit from a move constructor too, and move assignment too, since copying from an rvalue can be optimized, since we know the temporary is about to be destroyed when the assignment completes, we can "steal" its allocation and not have to create one of our own. This is fast, and also exception safe:
Data(Data&& other) :
local_data{other._local_data}
_size{other._size},
{
// important! This prevents other's destructor from
// deleting the allocation we just pilfered from it.
// Note, other's size and pointer are inconsistent, but it's
// about to be destroyed, so it doesn't matter. If it did,
// then swap both members, but that's needless more work
// in this case.
other._local_data = nullptr;
}
Data& operator=(Data&& other) {
_size = other._size;
swap(local_data, other.local_data);
return *this;
}
[updated to address this]
As for your main() function, the assertion does not look reasonable.
int main()
{
Data a("Some data");
auto ptr = a.data(); // Obtains a pointer to the original location
a = Data("New data");
assert(ptr == a.data()); // ????
return 0;
}
After you assign to a, the pointer should be different, and you should be asserting that the pointers are NOT the same. But in this case, ptr will be pointing to the old address that a held, which has been deleted by the time you get to the assertion. Storing pointers to object internals while modifying those objects is one of the basic recipes for errors.
One last thing: if you write an operator=, or a custom constructor, you almost always need a custom destructor. Always think of these three together as a special relationship. This was called the "Rule of Three": if you write any of them, you almost certainly must write all of them. The rule was expanded to the "Rule of Five" (after c++11) to include move constructors and move assignment. You should read up on these rules, and always think of these special member functions together. Another one to consider (not for this class, but in class design in general) is the best one, the Rule of Zero.
Having such simple program:
#include <iostream>
#include <string>
#include <windows.h>
using namespace std;
extern char MsgBuff[300];
class MyStr {
string* strPtr;
public:
// "normal" constructor
MyStr(const string& strPtr) : strPtr(new string(strPtr)) {}
// destructor
~MyStr() {
if(strPtr != NULL)
delete strPtr;
}
// copy constructor
MyStr(const MyStr& x) : strPtr(x.strPtr) {
OutputDebugStringA("copy constructor");
}
// move constructor
MyStr(MyStr&& x) : strPtr(x.strPtr) {
x.strPtr = nullptr;
OutputDebugStringA("copy constructor");
}
};
int main() {
MyStr foo("Exam");
MyStr foo2 = foo;
return 0;
}
The program throws an exception: Exception thrown: read access violation. As i invesigated it's caused by the destructor code - destroying these two objects (foo, foo2) we are freeing TWICE the same memory pointed by strPtr pointer.
How can this code be fixed to preserve the logic and avoid the exception?
A few things wrong with this code...
MyStr(const MyStr& x) : strPtr(x.strPtr) {
OutputDebugStringA("copy constructor");
}
This code makes "shallow" copy of the class as it only assigns adresses to existing object instead of creating a new one. This is the main problem, because as main() goes out of scope destructors will be called on all initialized objects. First ~foo will be called. "Succesfully". Then ~foo2 will be called and as it is still a valid object destructor will be called.
if (strPtr != NULL)
will pass, because nowhere in your code do you set strPtr to "nullptr" and so delete on uninitialized object will be called. This will cause the memory access violation.
Few things to keep in mind:
Please use std::string as much as possible. (people that implement this know what they're doing)
Never ever use raw pointers unless absolutely necessary. (this is just realy ugly way of doing things with really no benefits. Use std::shared_ptr and std::unique_ptr instead.)
Always set pointer to NULL after calling delete on it. (This goes without saying. Dont leave objects set to invalid adresses.)
NEVER.. use extern and/or global variables NEVERR!! (This just shows bad code design/structure)
Also this is not "bad" in "main" cpp file, but try to avoid using "using namespace std;". This will save you some headache when working with multiple namespaces.
Now for the "fixed" code part.. I assume you want to do a wrapper for string, so here you go:
#include <iostream>
#include <memory>
#include <string>
#include <windows.h>
class MyStr {
std::shared_ptr<std::string> m_ptrStr;
public:
// "normal" constructor
MyStr(const std::string& strPtr) : m_ptrStr(std::make_shared<std::string>(strPtr)) {}
// destructor
~MyStr() { }
// shallow copy constructor (you can do this since you have "smart" pointer)
MyStr(const MyStr& x) : m_ptrStr(x.m_ptrStr) {
OutputDebugStringA("copy constructor");
}
// move constructor
MyStr(MyStr&& x) noexcept : m_ptrStr(std::move(x.m_ptrStr)) {
OutputDebugStringA("move");
}
};
int main() {
MyStr foo("Exam");
MyStr foo2 = foo;
return 0;
}
The problem in the code is with the copy ctor.
// copy constructor
MyStr(const MyStr& x) : strPtr(x.strPtr) {
OutputDebugStringA("copy constructor");
}
Here you don't create a shallow copy of the string.
The solution is doing one of the following:
If memory and creation time is not an issue, create a deep copy by creating a new string and putting it inside of MyStr.
Using std::shared_ptr<std::String> instead of raw std::string*, which is a bit wasteful in my opinion.
Using copy-on-write approach. On copy, you don't create a new string, but rather another reference to the same resource, and as far as I know, this used to be the approach used by std::string. (you can read more about it on)
Another issue is using std::string since it is already doing exactly what you are trying to accomplish. If you want to make your own implementation, using raw pointers, use char * and not std::string *.
Presumably you're doing this to experiment with raw pointers as opposed to needing to house a string.
When using naked pointers, you need to properly implement copy constructors and assignment operators such that you perform a "deep copy" of the addressed data.
Whenever I do this, I write a "clone" method that would in your case perform:
void clone (MyStr const& src)
{
strPtr = new string(*src.strPtr);
}
Also I would recommend using the "free-and-nil" idiom to avoid double-deletion:
if (srcPtr)
{
delete srcPtr;
srcPtr = nullptr;
}
I recommend the Book "Professional C++" by Marc Gregoire which covers this kind of detail. I own copies of the 3rd and 4th editions. https://www.amazon.co.uk/Professional-C-Marc-Gregoire/dp/1119421306
I found unexpected (at least for me) behavior.
class A
{
char _text[100];
char* _beg;
char* _end;
public:
explicit A(char* text, size_t tsize) : _beg(&text[0]), _end(&text[std::min(tsize, 99)])
{
memcpy(_text, text, std::min(tsize, 99));
*_end = '\0';
}
inline std::string get_text()
{
return std::move(std::string(_beg, _end));
}
};
After that somewhere in code I do that:
A* add_A(A&& a)
{
list_a.push_back(std::move(a));
return &(list_a.back());
}
std::list<A> list_a;
{
add_A(A("my_text", 7));
list_a.back().get_text(); //returns "my_text"
}
list_a.back().get_text(); //returns trash
As only I move this class (using std::move), and call get_text() of object that was moved, I get trash, as if after movement address of variable _text changed, and so _beg and _end points to nowhere.
Does address of variables really can be changes after std::move (I thought move don't really move object, it was invented for that)?
If it can be changed, what is usual pattern to handle it (to change pointers accordingly)?
If it can't be change, may that behavior happens because I try to move such object to std::list (and so there somehow happens copying, it changes address of variables and makes pointers point to wrong positions)?
Moving in C++ is just a specialized form of copy, where you modify the data in the object being moved from. That's how unique_ptr works; you copy the pointer from one unique_ptr object to the other, then set the original value to NULL.
When you move an object, you are creating a new object, one who gets its data from another object. The address of members don't "change"; it's simply not the same object.
Because you didn't write a copy/move constructor, that means the compiler will write one for you. And all they do is copy each element. So the newly moved-to object will have pointers that point to the old object.
An object that is about to be destroyed.
It's like moving into a house that happens to look identical to your old one. No matter how much it looks like your old house, it isn't. You still have to change your address, since it's a new house. So too must the addresses of _beg and _end be updated.
Now, you could create a move constructor/assignment operator (along with a copy constructor/assignment operator) to update your pointers. But quite frankly, that's just wallpapering over bad design. It's not a good idea to have pointers to subobjects within the same object if you can help it. Instead of begin/end pointers, just have an actual size:
class A
{
char _text[100];
size_t _size;
public:
explicit A(char* text, size_t tsize) : _size(tsize)
{
strncpy(_text, text, 100);
}
inline std::string get_text()
{
return std::string(_text, _size); //Explicit `move` call is unnecessary
}
};
This way, there is no need to store begin/end pointers. Those can be synthesized as needed.
std::move has no moving parts, it simply promotes the input parameter to an rvalue reference -- remember that inside the body of foo(T&& t) { ... } the use of t by name evaluates as an lvalue (reference to rvalue).
inline std::string get_text()
{
return std::move(std::string(_beg, _end));
}
Breaking this down:
std::string(_beg, _end);
creates an anonymous, temporary std::string object constructed from _beg to _end. This is an rvalue.
std::move(...);
forcibly promotes this to an rvalue reference and prevents the compiler from performing return-value optimization. What you want is
return std::string(_beg, _end);
See assembly code comparison
You probably also want to use
list_a.emplace_back(std::move(a));
Unfortunately, there are two flaws in this approach.
The simpler is that the term moving can be a bit misleading, it sounds very one way. But in practice it is often a two way swap: the two objects exchange properties so that when the temporary object goes out of scope it performs cleanup of whatever the other object previously owned:
struct S {
char* s_;
S(const char* s) : s_(strdup(s)) {}
~S() { release(); }
void release() { if (s_) free(s_); }
S(const S& s) : s_(strdup(s.s_)) {}
S(S&& s) : s_(s.s_) { s.s_ = nullptr; }
S& operator=(const S& s) { release(); s_ = strdup(s); return *this; }
S& operator=(S&& s) { std::swap(s_, s.s_); return *this; }
};
Note this line:
S& operator=(S&& s) { std::swap(s_, s.s_); return *this; }
When we write:
S s1("hello");
s1 = S("world");
the second line invokes the move-assignment operator. The pointer for the copy of hello is moved into the temporary, the temporary goes out of scope and is destroyed, the copy of "hello" is freed.
Doing this swap with your array of characters is significantly less efficient than a one-way copy would be:
struct S {
char s_[100];
S(const S& s) {
std::copy(std::begin(s.s_), std::end(s.s_), std::begin(s_));
}
S(S&& s) {
char t_[100];
std::copy(std::begin(s.s_), std::end(s.s_), std::begin(t_));
std::copy(std::begin(s_), std::end(s_), std::begin(s.s_));
std::copy(std::begin(t_), std::end(t_), std::end(s_));
}
};
You don't have to do this, the rvalue parameter only needs to be in a safe to destroy state, but the above is what the default move operators are going to do.
The disasterous part of your code is that the default move operator is naive.
struct S {
char text_[100];
char *beg_, *end_;
S() : beg_(text_), end_(text_ + 100) {}
};
Consider the following copy-construction:
S s(S());
What does s.beg_ point to?
Answer: it points to S().text_, not s.text_. You would need to write a copy constructor that copied the contents of text_ and then pointed its own beg_ and end_ to its own text_ rather than copying the source values.
The same problem occurs with the move operator: it will move the contents of text_ but it will also move the pointers, and have no clue that they are relative.
You'll either need to write copy/move constructors and assignment operators, or you could consider replacing beg_ and end_ with a single size_t size value.
But in either case, move is not your friend here: you're not transferring ownership or performing a shallow copy, all of your data is inside your object.
Our professor posted a custom 'String' template file online, and asked us a while ago to fill out the functions below. My question, in order to try and understand this, is why the top three constructors have Text = NULL; and below it, this = source;, some other form of it. I feel like each should say Text = the_input_parameter.
Many thanks, here's the code:
class String
{
public:
// Default constructor
String()
{
Text = NULL;
}
String(const String& source)
{
Text = NULL;
// Call the assignment operator to perform deep copy
*this = source;
}
String(const char* text)
{
Text = NULL;
// Call the assignment operator to perform deep copy
*this = text;
}
~String()
{
delete[] Text;
}
// Assignment operator to perform deep copy
String& operator = (const char* text)
{
// Ddispose of old Text
delete[] Text;
// +1 accounts for NULL-terminator
int trueLength = GetLength(text) + 1;
// Dynamically allocate characters on heap
Text = new char[trueLength];
// Copy all characters from source to Text; +1 accounts for NULL-terminator
for ( int i = 0; i < trueLength; i++ )
Text[i] = text[i];
return *this;
}
// Returns a reference to a single character from this String
char& operator [] (int index) const
{
int length = GetLength();
// Check for valid index
if ( (index < 0) || (index > length) )
{
stringstream error;
error << "operator[] - index " << index << " is out of bounds (0.." << (length - 1) << ")";
throw String(error.str().c_str());
}
return Text[index];
}
private:
// The encapsulated C-string
char* Text;
};
Why you should not implement constructors in terms of assignment:
It gets pretty nasty in derived classes. Think about it.
It's difficult to make exception safe.
It's also inefficient, to boot (requiring default construction then assignment).
So the answer to why it’s done that way in your example code, is possibly that your professor doesn’t know diddly about C++ programming.
Otherwise, it's difficult to say: it just doesn't make any sense at all to do that.
Going the other way, however, namely implementing copy assignment in terms of copy construction, is very common and is known as the copy-and-swap idiom.
It's simple, exceptions safe and generally efficient, and goes like this:
class Foo
{
public:
void swap_with( Foo& other ) throw()
{
// No-throwing swap here.
}
void operator=( Foo other )
{
other.swap_with( *this );
}
};
yup, that's all.
variants include naming the swapper just swap, and letting the assignment operator return a reference, and some prefer to pass the argument by reference and then make a copy (using copy construction).
It's just a way to factor out common code into a helper function. In this case, operator=() acts as the helper function. Its function is to deallocate the current string (in this case, NULL) and perform a deep copy of the right-hand side.
I feel like each should say Text = the_input_parameter.
For String(const String& source) this wouldn't compile since source isn't the right type.
For String(const char* text) this wouldn't be correct since that would just assign the pointer instead of performing a deep copy.
The above assumes you are only showing us a part of the class, and that the actual class defines an appropriate assignment operator and a destructor. If it doesn't, you need a new professor.
The class manages memory, so destructor releases it, and assignment operator allocates new for new data and releases old one (shoud be in that order).
Then the explanation of initial assignment is clear: you need to initialize the member field to a correct value, as otherwise it will contain garbage (some pointer to somewhere), which code will attempt to use and free.
Though it is not seen from the code, there might be also assignment for const String& and type cast operator operator const char *() const.
I've been exploring the possibilities of Move Constructors in C++, and I was wondering what are some ways of taking advantage of this feature in an example such as below. Consider this code:
template<unsigned int N>
class Foo {
public:
Foo() {
for (int i = 0; i < N; ++i) _nums[i] = 0;
}
Foo(const Foo<N>& other) {
for (int i = 0; i < N; ++i) _nums[i] = other._nums[i];
}
Foo(Foo<N>&& other) {
// ??? How can we take advantage of move constructors here?
}
// ... other methods and members
virtual ~Foo() { /* no action required */ }
private:
int _nums[N];
};
Foo<5> bar() {
Foo<5> result;
// Do stuff with 'result'
return result;
}
int main() {
Foo<5> foo(bar());
// ...
return 0;
}
In this above example, if we trace the program (with MSVC++ 2011), we see that Foo<N>::Foo(Foo<N>&&) is called when constructing foo, which is the desired behaviour. However, if we didn't have Foo<N>::Foo(Foo<N>&&), Foo<N>::Foo(const Foo<N>&) would be called instead, which would do a redundant copy operation.
My question is, as noted in the code, with this specific example which is using a statically-allocated simple array, is there any way to utilize the move constructor to avoid this redundant copy?
First off, there's a general sort of advice that says you shouldn't write any copy/move constructor, assignment operator or destructor at all if you can help it, and rather compose your class of high-quality components which in turn provide these, allowing the default-generated functions to Do The Right Thing. (The reverse implication is that if you do have to write any one of those, you probably have to write all of them.)
So the question boils down to "which single-responsibility component class can take advantage of move semantics?" The general answer is: Anything that manages a resource. The point is that the move constructor/assigner will just reseat the resource to the new object and invalidate the old one, thus avoiding the (presumed expensive or impossible) new allocation and deep copying of the resource.
The prime example is anything that manages dynamic memory, where the move operation simply copies the pointer and sets the old object's pointer to zero (so the old object's destructor does nothing). Here's a naive example:
class MySpace
{
void * addr;
std::size_t len;
public:
explicit MySpace(std::size_t n) : addr(::operator new(n)), len(n) { }
~MySpace() { ::operator delete(addr); }
MySpace(const MySpace & rhs) : addr(::operator new(rhs.len)), len(rhs.len)
{ /* copy memory */ }
MySpace(MySpace && rhs) : addr(rhs.addr), len(rhs.len)
{ rhs.len = 0; rhs.addr = 0; }
// ditto for assignment
};
The key is that any copy/move constructor will do a full copying of the member variables; it is only when those variables are themselves handles or pointers to resources that you can avoid copying the resource, because of the agreement that a moved object is no longer considered valid and that you're free to steal from it. If there's nothing to steal, then there's no benefit in moving.
In this case it's not useful because int has no move-constructors.
However, it could be useful if those were strings instead, for example:
template<unsigned int N>
class Foo {
public:
// [snip]
Foo(Foo<N>&& other) {
// move each element from other._nums to _nums
std::move(std::begin(other._nums), std::end(other._nums), &_nums[0]);
}
// [snip]
private:
std::string _nums[N];
};
Now you avoid copying strings where a move will do. I'm not sure if a conforming C++11 compiler will generate equivalent code if you omit all the copy-/move-constructors completely, sorry.
(In other words, I'm not sure if std::move is specially defined to do an element-wise move for arrays.)
For the class template you wrote, there's no advantage to take in a move constructor.
There would be an advantage if the member array was allocated dynamically. But with a plain array as a member, there's nothing to optimize, you can only copy the values. There's no way to move them.
Usually, move-semantic is implemented when your class manages resource. Since in your case, the class doesn't manages resource, the move-semantic would be more like copy-semantic, as there is nothing to be moved.
To better understand when move-semantic becomes necessary, consider making _nums a pointer, instead of an array:
template<unsigned int N>
class Foo {
public:
Foo()
{
_nums = new int[N](); //allocate and zeo-initialized
}
Foo(const Foo<N>& other)
{
_nums = new int[N];
for (int i = 0; i < N; ++i) _nums[i] = other._nums[i];
}
Foo(Foo<N>&& other)
{
_nums = other._nums; //move the resource
other._nums=0; //make it null
}
Foo<N> operator=(const Foo<N> & other); //implement it!
virtual ~Foo() { delete [] _nums; }
private:
int *_nums;
};