Separate move- and copy-assignment, instead of assignment from pass-by-value: Meh, the list class itself isn't big enough to worry about it, really, and when it gets inlined - it should - there shouldn't be a difference.
A potentially throwing dtor is in nearly every case pathological. Trying to deal with it in any useful way is a supremely frustrating exercise in futility.
Well,
std::swap()
uses the move-ctor, move-assignment, and the dtor. As your move-ctor is properly implemented withswap(a, b)
, what would you expect to happen if you didn't implement your ownswap(a, b)
? (As an aside,using std::swap;
there isn't very sensible, and rings a big alarm for any reader.)Or are you referring to not using
using std::swap; swap(x, y);
in the implementation of your customswap()
? In that case, I really have to extol the value of conciseness and using the appropriate abstractions, all else being equal.
Or are you referring to not using using std::swap; swap(x, y);
in the implementation of your custom swap()
? In that case, I really have to extol the value of conciseness and using the appropriate abstractions, all else being equal.
- Nice to hear you will add iterator-support. Anyway, that will lead to a total re-design of: Copy-ctor, initializer_list-ctor, and
.remove()
.Nice to hear you will add iterator-support. Anyway, that will lead to a total re-design of: Copy-ctor, initializer_list-ctor, and
.remove()
.
In the definition of the template-class
List<T>
, you can refer to the current template-instantiation without mentioning the template-argument with a simpleList
.Embrace
auto
. No checking required to know whether you remembered to use the right type, or fix it in hundreds of places when you decide to change it for any reason.Personally, I always put the pointers before the data-member, but that may just be me.
Remember that you can make a pointer to any member, even if that is of pointer-type. This one point has to be applied over and over and over again, to remove lots of special cases and the concomittant duplication.
Another way to achieve the same is using a sentinel. You have to take care with it to avoid a dummy
T
though, or any spurious additional allocation. Might want to move the pointer from the node into a base-class for that.It's curious to implement the dtor in terms of
.clear()
, instead of the other way around. That might potentially lead to the former being less efficient due to favoring the latter, which is less than ideal...Don't test for self-assignment. While self-copy-assignment must work, there's no need to pessimize the case that matters by going out of your way to optimize for an extreme edge-case.
Don't test for self-move-assignment. Nearly the same as for self-copy-assignment applies, though self-move-assignment is pathological, and you might want to stick an
assert(this != &rhs);
in there..emplace()
should construct the value in-place, emplacing it. That's what it says on the tin.
Aside from that, the two.insert()
s should delegate to the single.emplace()
for all the work, not the other way around, to reduce needless code-duplication.
Another way to achieve the same is using a sentinel. You have to take care with it to avoid a dummy T
though, or any spurious additional allocation. Might want to move the pointer from the node into a base-class for that.
It's curious to implement the dtor in terms of
.clear()
, instead of the other way around. That might potentially lead to the former being less efficient due to favoring the latter, which is less than ideal...Don't test for self-assignment. While self-copy-assignment must work, there's no need to pessimize the case that matters by going out of your way to optimize for an extreme edge-case.
Don't test for self-move-assignment. Nearly the same as for self-copy-assignment applies, though self-move-assignment is pathological, and you might want to stick an
assert(this != &rhs);
in there..emplace()
should construct the value in-place, emplacing it. That's what it says on the tin.
Aside from that, the two.insert()
s should delegate to the single.emplace()
for all the work, not the other way around, to reduce needless code-duplication.
Separate move- and copy-assignment, instead of assignment from pass-by-value: Meh, the list class itself isn't big enough to worry about it, really, and when it gets inlined - it should - there shouldn't be a difference.
A potentially throwing dtor is in nearly every case pathological. Trying to deal with it in any useful way is a supremely frustrating exercise in futility.
Well,
std::swap()
uses the move-ctor, move-assignment, and the dtor. As your move-ctor is properly implemented withswap(a, b)
, what would you expect to happen if you didn't implement your ownswap(a, b)
? (As an aside,using std::swap;
there isn't very sensible, and rings a big alarm for any reader.)
Or are you referring to not using using std::swap; swap(x, y);
in the implementation of your custom swap()
? In that case, I really have to extol the value of conciseness and using the appropriate abstractions, all else being equal.
- Nice to hear you will add iterator-support. Anyway, that will lead to a total re-design of: Copy-ctor, initializer_list-ctor, and
.remove()
.
In the definition of the template-class
List<T>
, you can refer to the current template-instantiation without mentioning the template-argument with a simpleList
.Embrace
auto
. No checking required to know whether you remembered to use the right type, or fix it in hundreds of places when you decide to change it for any reason.Personally, I always put the pointers before the data-member, but that may just be me.
Remember that you can make a pointer to any member, even if that is of pointer-type. This one point has to be applied over and over and over again, to remove lots of special cases and the concomittant duplication.
Another way to achieve the same is using a sentinel. You have to take care with it to avoid a dummy T
though, or any spurious additional allocation. Might want to move the pointer from the node into a base-class for that.
It's curious to implement the dtor in terms of
.clear()
, instead of the other way around. That might potentially lead to the former being less efficient due to favoring the latter, which is less than ideal...Don't test for self-assignment. While self-copy-assignment must work, there's no need to pessimize the case that matters by going out of your way to optimize for an extreme edge-case.
Don't test for self-move-assignment. Nearly the same as for self-copy-assignment applies, though self-move-assignment is pathological, and you might want to stick an
assert(this != &rhs);
in there..emplace()
should construct the value in-place, emplacing it. That's what it says on the tin.
Aside from that, the two.insert()
s should delegate to the single.emplace()
for all the work, not the other way around, to reduce needless code-duplication.
Separate move- and copy-assignment, instead of assignment from pass-by-value: Meh, the list class itself isn't big enough to worry about it, really, and when it gets inlined - it should - there shouldn't be a difference.
A potentially throwing dtor is in nearly every case pathological. Trying to deal with it in any useful way is a supremely frustrating exercise in futility.
Well,
std::swap()
uses the move-ctor, move-assignment, and the dtor. As your move-ctor is properly implemented withswap(a, b)
, what would you expect to happen if you didn't implement your ownswap(a, b)
? (As an aside,using std::swap;
there isn't very sensible, and rings a big alarm for any reader.)Or are you referring to not using
using std::swap; swap(x, y);
in the implementation of your customswap()
? In that case, I really have to extol the value of conciseness and using the appropriate abstractions, all else being equal.Nice to hear you will add iterator-support. Anyway, that will lead to a total re-design of: Copy-ctor, initializer_list-ctor, and
.remove()
.
In the definition of the template-class
List<T>
, you can refer to the current template-instantiation without mentioning the template-argument with a simpleList
.Embrace
auto
. No checking required to know whether you remembered to use the right type, or fix it in hundreds of places when you decide to change it for any reason.Personally, I always put the pointers before the data-member, but that may just be me.
Remember that you can make a pointer to any member, even if that is of pointer-type. This one point has to be applied over and over and over again, to remove lots of special cases and the concomittant duplication.
Another way to achieve the same is using a sentinel. You have to take care with it to avoid a dummy
T
though, or any spurious additional allocation. Might want to move the pointer from the node into a base-class for that.It's curious to implement the dtor in terms of
.clear()
, instead of the other way around. That might potentially lead to the former being less efficient due to favoring the latter, which is less than ideal...Don't test for self-assignment. While self-copy-assignment must work, there's no need to pessimize the case that matters by going out of your way to optimize for an extreme edge-case.
Don't test for self-move-assignment. Nearly the same as for self-copy-assignment applies, though self-move-assignment is pathological, and you might want to stick an
assert(this != &rhs);
in there..emplace()
should construct the value in-place, emplacing it. That's what it says on the tin.
Aside from that, the two.insert()
s should delegate to the single.emplace()
for all the work, not the other way around, to reduce needless code-duplication.
In the definition of the template-class
List<T>
, you can refer to the current template-instantiation without mentioning the template-argument with a simpleList
.Embrace
auto
. No checking required to know whether you remembered to use the right type, or fix it in hundreds of places when you decide to change it for any reason.Personally, I always put the pointers before the data-member, but that may just be me.
Remember that you can make a pointer to any member, even if that is of pointer-type. This one point has to be applied over and over and over again.
It's curious to implement the dtor in terms of
.clear()
, instead of the other way around. That might potentially lead to the former being less efficient due to favoring the latter, which is less than ideal...Don't test for self-assignment. While self-copy-assignment must work, there's no need to pessimize the case that matters by going outremove lots of your way to optimize for an extreme edge-case.
Don't test for self-move-assignment. Nearly the same as for self-copy-assignment applies, though self-move-assignment is pathological,special cases and you might want to stick an
assert(this != &rhs);
in there..emplace()
should construct the value in-place, emplacing it. That's what it says on the tin.
Aside from that, the two.insert()
s should delegate to the single.emplace()
for all the work, not the other way around, to reduce needless code-duplicationconcomittant duplication.
Another way to achieve the same is using a sentinel. You have to take care with it to avoid a dummy T
though, or any spurious additional allocation. Might want to move the pointer from the node into a base-class for that.
It's curious to implement the dtor in terms of
.clear()
, instead of the other way around. That might potentially lead to the former being less efficient due to favoring the latter, which is less than ideal...Don't test for self-assignment. While self-copy-assignment must work, there's no need to pessimize the case that matters by going out of your way to optimize for an extreme edge-case.
Don't test for self-move-assignment. Nearly the same as for self-copy-assignment applies, though self-move-assignment is pathological, and you might want to stick an
assert(this != &rhs);
in there..emplace()
should construct the value in-place, emplacing it. That's what it says on the tin.
Aside from that, the two.insert()
s should delegate to the single.emplace()
for all the work, not the other way around, to reduce needless code-duplication.
In the definition of the template-class
List<T>
, you can refer to the current template-instantiation without mentioning the template-argument with a simpleList
.Embrace
auto
. No checking required to know whether you remembered to use the right type, or fix it in hundreds of places when you decide to change it for any reason.Personally, I always put the pointers before the data-member, but that may just be me.
Remember that you can make a pointer to any member, even if that is of pointer-type. This one point has to be applied over and over and over again.
It's curious to implement the dtor in terms of
.clear()
, instead of the other way around. That might potentially lead to the former being less efficient due to favoring the latter, which is less than ideal...Don't test for self-assignment. While self-copy-assignment must work, there's no need to pessimize the case that matters by going out of your way to optimize for an extreme edge-case.
Don't test for self-move-assignment. Nearly the same as for self-copy-assignment applies, though self-move-assignment is pathological, and you might want to stick an
assert(this != &rhs);
in there..emplace()
should construct the value in-place, emplacing it. That's what it says on the tin.
Aside from that, the two.insert()
s should delegate to the single.emplace()
for all the work, not the other way around, to reduce needless code-duplication.
In the definition of the template-class
List<T>
, you can refer to the current template-instantiation without mentioning the template-argument with a simpleList
.Embrace
auto
. No checking required to know whether you remembered to use the right type, or fix it in hundreds of places when you decide to change it for any reason.Personally, I always put the pointers before the data-member, but that may just be me.
Remember that you can make a pointer to any member, even if that is of pointer-type. This one point has to be applied over and over and over again, to remove lots of special cases and the concomittant duplication.
Another way to achieve the same is using a sentinel. You have to take care with it to avoid a dummy T
though, or any spurious additional allocation. Might want to move the pointer from the node into a base-class for that.
It's curious to implement the dtor in terms of
.clear()
, instead of the other way around. That might potentially lead to the former being less efficient due to favoring the latter, which is less than ideal...Don't test for self-assignment. While self-copy-assignment must work, there's no need to pessimize the case that matters by going out of your way to optimize for an extreme edge-case.
Don't test for self-move-assignment. Nearly the same as for self-copy-assignment applies, though self-move-assignment is pathological, and you might want to stick an
assert(this != &rhs);
in there..emplace()
should construct the value in-place, emplacing it. That's what it says on the tin.
Aside from that, the two.insert()
s should delegate to the single.emplace()
for all the work, not the other way around, to reduce needless code-duplication.
First, about your points:
Separate move- and copy-assignment, instead of assignment from pass-by-value: Meh, the list class itself isn't big enough to worry about it, really, and when it gets inlined - it should - there shouldn't be a difference.
A potentially throwing dtor is in nearly every case pathological. Trying to deal with it in any useful way is a supremely frustrating exercise in futility.
Well,
std::swap()
uses the move-ctor, move-assignment, and the dtor. As your move-ctor is properly implemented withswap(a, b)
, what would you expect to happen if you didn't implement your ownswap(a, b)
? (As an aside,using std::swap;
there isn't very sensible, and rings a big alarm for any reader.)
Or are you referring to not using using std::swap; swap(x, y);
in the implementation of your custom swap()
? In that case, I really have to extol the value of conciseness and using the appropriate abstractions, all else being equal.
- Nice to hear you will add iterator-support. Anyway, that will lead to a total re-design of: Copy-ctor, initializer_list-ctor, and
.remove()
.
Now, other things:
In the definition of the template-class
List<T>
, you can refer to the current template-instantiation without mentioning the template-argument with a simpleList
.Embrace
auto
. No checking required to know whether you remembered to use the right type, or fix it in hundreds of places when you decide to change it for any reason.Personally, I always put the pointers before the data-member, but that may just be me.
Remember that you can make a pointer to any member, even if that is of pointer-type. This one point has to be applied over and over and over again.
It's curious to implement the dtor in terms of
.clear()
, instead of the other way around. That might potentially lead to the former being less efficient due to favoring the latter, which is less than ideal...Don't test for self-assignment. While self-copy-assignment must work, there's no need to pessimize the case that matters by going out of your way to optimize for an extreme edge-case.
Don't test for self-move-assignment. Nearly the same as for self-copy-assignment applies, though self-move-assignment is pathological, and you might want to stick an
assert(this != &rhs);
in there..emplace()
should construct the value in-place, emplacing it. That's what it says on the tin.
Aside from that, the two.insert()
s should delegate to the single.emplace()
for all the work, not the other way around, to reduce needless code-duplication.