Deleting raw pointers from std::vector
Clash Royale CLAN TAG#URR8PPP
I have following pattern:
- I have a
std::vector
containing raw pointers to objects (I know raw pointers are "evil", but it's legacy software needing to be maintained). - Now for each element in the vector I need to do a test and if the test is positive do something with the pointer, delete it and then remove it from the vector:
Pseudo code:
for each pointer in vector
if (SomeTest(pointer))
DoSomething(pointer)
delete pointer
remove pointer from vector
I'm unable to come up with some nice clean code for this.
This link provides different approaches, but they all look more or less cumbersome to me.
Cumbersome solution I'm using now:
for(auto & p : v)
if (SomeTest(p))
DoSomething(p);
delete p;
p = nullptr;
v.erase(std::remove(v.begin(), v.end(), nullptr), v.end());
c++ stdvector
|
show 4 more comments
I have following pattern:
- I have a
std::vector
containing raw pointers to objects (I know raw pointers are "evil", but it's legacy software needing to be maintained). - Now for each element in the vector I need to do a test and if the test is positive do something with the pointer, delete it and then remove it from the vector:
Pseudo code:
for each pointer in vector
if (SomeTest(pointer))
DoSomething(pointer)
delete pointer
remove pointer from vector
I'm unable to come up with some nice clean code for this.
This link provides different approaches, but they all look more or less cumbersome to me.
Cumbersome solution I'm using now:
for(auto & p : v)
if (SomeTest(p))
DoSomething(p);
delete p;
p = nullptr;
v.erase(std::remove(v.begin(), v.end(), nullptr), v.end());
c++ stdvector
10
sorry, nitpick: raw pointers to objects owned by someone else are rather harmless. Only owning raw pointers are evil
– user463035818
Feb 11 at 14:04
1
The only concern I can think of is invalidating the iterator, and messing up your loop, which wouldn't have anything to do with the pointer. Perhaps you could post a Minimal, Complete, and Verifiable example. (consider std::remove_if ?)
– Kenny Ostrom
Feb 11 at 14:04
5
What specifically is wrong with the erase_if?
– Useless
Feb 11 at 14:07
1
@user463035818 Needless to say,delete
ing raw pointers only makes sense specifically in the evil case of owning raw pointers. The statement in the question does not explicitly attribute ownership to the pointers so you're technically correct to nitpick.
– Max Langhof
Feb 11 at 14:13
4
@MaxLanghof if the vector is responsible todelete
them then I would say the vector owns them. Anyhow, I was specifically refering to "I know raw pointers are "evil"" and I am a bit picky whem something gets the "evil" attribute. Too easily this can lead to misunderstanding
– user463035818
Feb 11 at 14:15
|
show 4 more comments
I have following pattern:
- I have a
std::vector
containing raw pointers to objects (I know raw pointers are "evil", but it's legacy software needing to be maintained). - Now for each element in the vector I need to do a test and if the test is positive do something with the pointer, delete it and then remove it from the vector:
Pseudo code:
for each pointer in vector
if (SomeTest(pointer))
DoSomething(pointer)
delete pointer
remove pointer from vector
I'm unable to come up with some nice clean code for this.
This link provides different approaches, but they all look more or less cumbersome to me.
Cumbersome solution I'm using now:
for(auto & p : v)
if (SomeTest(p))
DoSomething(p);
delete p;
p = nullptr;
v.erase(std::remove(v.begin(), v.end(), nullptr), v.end());
c++ stdvector
I have following pattern:
- I have a
std::vector
containing raw pointers to objects (I know raw pointers are "evil", but it's legacy software needing to be maintained). - Now for each element in the vector I need to do a test and if the test is positive do something with the pointer, delete it and then remove it from the vector:
Pseudo code:
for each pointer in vector
if (SomeTest(pointer))
DoSomething(pointer)
delete pointer
remove pointer from vector
I'm unable to come up with some nice clean code for this.
This link provides different approaches, but they all look more or less cumbersome to me.
Cumbersome solution I'm using now:
for(auto & p : v)
if (SomeTest(p))
DoSomething(p);
delete p;
p = nullptr;
v.erase(std::remove(v.begin(), v.end(), nullptr), v.end());
c++ stdvector
c++ stdvector
edited Feb 11 at 14:15
Jabberwocky
asked Feb 11 at 14:01
JabberwockyJabberwocky
27.8k93774
27.8k93774
10
sorry, nitpick: raw pointers to objects owned by someone else are rather harmless. Only owning raw pointers are evil
– user463035818
Feb 11 at 14:04
1
The only concern I can think of is invalidating the iterator, and messing up your loop, which wouldn't have anything to do with the pointer. Perhaps you could post a Minimal, Complete, and Verifiable example. (consider std::remove_if ?)
– Kenny Ostrom
Feb 11 at 14:04
5
What specifically is wrong with the erase_if?
– Useless
Feb 11 at 14:07
1
@user463035818 Needless to say,delete
ing raw pointers only makes sense specifically in the evil case of owning raw pointers. The statement in the question does not explicitly attribute ownership to the pointers so you're technically correct to nitpick.
– Max Langhof
Feb 11 at 14:13
4
@MaxLanghof if the vector is responsible todelete
them then I would say the vector owns them. Anyhow, I was specifically refering to "I know raw pointers are "evil"" and I am a bit picky whem something gets the "evil" attribute. Too easily this can lead to misunderstanding
– user463035818
Feb 11 at 14:15
|
show 4 more comments
10
sorry, nitpick: raw pointers to objects owned by someone else are rather harmless. Only owning raw pointers are evil
– user463035818
Feb 11 at 14:04
1
The only concern I can think of is invalidating the iterator, and messing up your loop, which wouldn't have anything to do with the pointer. Perhaps you could post a Minimal, Complete, and Verifiable example. (consider std::remove_if ?)
– Kenny Ostrom
Feb 11 at 14:04
5
What specifically is wrong with the erase_if?
– Useless
Feb 11 at 14:07
1
@user463035818 Needless to say,delete
ing raw pointers only makes sense specifically in the evil case of owning raw pointers. The statement in the question does not explicitly attribute ownership to the pointers so you're technically correct to nitpick.
– Max Langhof
Feb 11 at 14:13
4
@MaxLanghof if the vector is responsible todelete
them then I would say the vector owns them. Anyhow, I was specifically refering to "I know raw pointers are "evil"" and I am a bit picky whem something gets the "evil" attribute. Too easily this can lead to misunderstanding
– user463035818
Feb 11 at 14:15
10
10
sorry, nitpick: raw pointers to objects owned by someone else are rather harmless. Only owning raw pointers are evil
– user463035818
Feb 11 at 14:04
sorry, nitpick: raw pointers to objects owned by someone else are rather harmless. Only owning raw pointers are evil
– user463035818
Feb 11 at 14:04
1
1
The only concern I can think of is invalidating the iterator, and messing up your loop, which wouldn't have anything to do with the pointer. Perhaps you could post a Minimal, Complete, and Verifiable example. (consider std::remove_if ?)
– Kenny Ostrom
Feb 11 at 14:04
The only concern I can think of is invalidating the iterator, and messing up your loop, which wouldn't have anything to do with the pointer. Perhaps you could post a Minimal, Complete, and Verifiable example. (consider std::remove_if ?)
– Kenny Ostrom
Feb 11 at 14:04
5
5
What specifically is wrong with the erase_if?
– Useless
Feb 11 at 14:07
What specifically is wrong with the erase_if?
– Useless
Feb 11 at 14:07
1
1
@user463035818 Needless to say,
delete
ing raw pointers only makes sense specifically in the evil case of owning raw pointers. The statement in the question does not explicitly attribute ownership to the pointers so you're technically correct to nitpick.– Max Langhof
Feb 11 at 14:13
@user463035818 Needless to say,
delete
ing raw pointers only makes sense specifically in the evil case of owning raw pointers. The statement in the question does not explicitly attribute ownership to the pointers so you're technically correct to nitpick.– Max Langhof
Feb 11 at 14:13
4
4
@MaxLanghof if the vector is responsible to
delete
them then I would say the vector owns them. Anyhow, I was specifically refering to "I know raw pointers are "evil"" and I am a bit picky whem something gets the "evil" attribute. Too easily this can lead to misunderstanding– user463035818
Feb 11 at 14:15
@MaxLanghof if the vector is responsible to
delete
them then I would say the vector owns them. Anyhow, I was specifically refering to "I know raw pointers are "evil"" and I am a bit picky whem something gets the "evil" attribute. Too easily this can lead to misunderstanding– user463035818
Feb 11 at 14:15
|
show 4 more comments
7 Answers
7
active
oldest
votes
As often the answer is: know your <algorithm>
s (and is a good reminder to myself) ;)
std::partition
is what you're looking for: std::partition(begin, end, p)
"moves" the elements of the range [begin
, end
) which do not satisfy the predicate p
at the end of the range; you can then treat them as a batch.
auto const to_be_removed = std::partition(begin(v), end(v), (auto p) /* predicate */ );
std::for_each(to_be_removed, end(v), (auto p)
/* crunch */
delete p;
);
v.erase(to_be_removed, end(v));
Full program
#include <iostream>
#include <algorithm>
#include <vector>
int main()
std::vector v = new int0, new int1, new int2 ;
// let's delete all even values
auto const to_be_removed = std::partition(begin(v), end(v), (auto p) return *p % 2 != 0; );
std::for_each(to_be_removed, end(v), (auto p)
std::cout << "Deleting value " << *p << "...n";
delete p;
);
v.erase(to_be_removed, end(v));
Live demo
To go further
This implementation has two major drawbacks: the order from the vector is not stable (1), it could be factored into a reusable function (2).
- (1) is solved by
std::stable_partition
. - (2) is not that hard:
template<class InputIt, class UnaryPredicate, class UnaryDeleter>
InputIt delete_if(InputIt begin, InputIt end, UnaryPredicate p, UnaryDeleter d)
auto const to_be_removed = std::stable_partition(begin, end, std::not_fn(p));
std::for_each(to_be_removed, end, [d](auto p) d(p) ; delete p; );
return to_be_removed;
template<class Container, class UnaryPredicate, class UnaryDeleter>
auto delete_if(Container& c, UnaryPredicate p, UnaryDeleter d)
using std::begin, std::end;
return c.erase(delete_if(begin(c), end(c), p, d), end(c));
Usage:
delete_if(v, SomeTest, DoSomething);
Live demo
Also,delete p
should be handled by theUnaryDeleter
, otherwise it can only be used for dynamic pointers.
– user202729
Feb 11 at 16:13
I disagree on that point; my reasoning is that we're solving OP's specific problem: "for each element in the vector I need to do a test and if the test is positive do something with the [raw] pointer, delete it and then remove it from the vector". There's no reason for them to not put thedelete
statement on thedelete_if
function.
– YSC
Feb 11 at 16:24
add a comment |
You can use std::remove_if
I am not sure why the article you linked uses std::remove_if
before deleting the pointers because that won't work. You have to delete the pointers before the removal:
std::vector<int*> v;
v.erase(std::remove_if(std::begin(v), std::end(v), (int* p)
// do your test and do not remove on failure
if(!SomeTest(p))
return false; // keep this one
DoSomething(p);
// Now we remove but be sure to delete here, before the
// element is moved (and therefore invalidated)
delete p;
return true; // signal for removal
), std::end(v));
Notes: Why this is safe.
Deleting the pointer does not modify the pointer itself, but the object being pointed to. That means that no elements are modified with this approach.
The standard at C++17 28.6.8 5
guarantees that the predicate will be called just once for each element.
Responding to your comment, yes: that's a good idea for this specific case. (+1)
– YSC
Feb 11 at 14:53
add a comment |
The simplest solution - starting from the linked article - is to take the erase_if
function
template <typename Container, typename Pred>
void erase_if(Container &c, Pred p)
c.erase(std::remove_if(std::begin(c), std::end(c), p), std::end(c));
and just call it with
erase_if(v, (T *pointer)
if (SomeTest(pointer))
DoSomething(pointer);
delete pointer;
return true; //remove pointer from vector
return false;
);
You can obviously split your predicate in two if you want to separate the SomeTest/DoSomething part from the delete
part:
template <typename Container, typename Pred>
void delete_if(Container &c, Pred p)
auto e = std::remove_if(std::begin(c), std::end(c),
[&p](Container::value_type *pointer)
if (p(pointer))
delete pointer;
return true;
return false;
);
c.erase(e, std::end(c));
Since you haven't said why you don't like the erase_if
you linked yourself, I can't guess whether this has the same problem.
1
There's an issue with this approach:std::erase_if(...)
is equivalent tov.erase(std::remove_if(...))
, which explicitly specifies that the predicate must not modify the element.
– You
Feb 11 at 15:37
@You Technically it doesn't modify the element, but I guess the spirit is thatp
should be idempotent (callable many times without harm), and this is certainly not so in this case as the next call on the deleted pointer will probably have UB.
– Arne Vogel
Feb 11 at 15:44
Fair point. (I believe the exact wording is "The [predicate] shall not apply any non-constant function through the dereferenced iterator.", whichdelete
ing technically doesn't do I guess since it's fine todelete
a const pointer.)
– You
Feb 11 at 15:49
@ArneVogel The standard guarantees the predicate will only be called once per element
– Galik
Feb 11 at 17:12
I assume you are deducing this from (alg.remove/5) – IMO it's still a bit of a technicality. If I was doing a code review, I'd suggest switching topartition
/stable_partition
or writing a meaningful comment for why it is correct.
– Arne Vogel
Feb 11 at 20:44
add a comment |
With the following approach first you split the elements to be deleted, then you delete, then you adjust the vector.
auto badIt = std::stable_partition(std::beging(v), std::end(v), SomeTestInverse);
std::for_each(badIt, std::end(v), (auto e) DoSomething(e); delete e;);
v.erase(badIt,std::end(v));
The predicate provided must be true for element to keep in order to work, as elements failing the predicate are in the last range.
add a comment |
Assumed you have a vector of int pointer. Here is my solution:
vector<int*> vpi;
for (vector<int*>::iterator it = vpi.begin(); it != vpi.end(); )
if (SomeTest(*it))
DoSomething(*it)
int* old = *it;
it = vpi.erase(it);
delete old;
else
it++;
3
not sure but as I understood the question this is the code that OP would like to avoid writing.
– user463035818
Feb 11 at 14:10
@LocTran I usually doit = vector.erase(it)
.. not so nice as your old, new :)
– Samer Tufail
Feb 11 at 14:15
3
Why useold
?DoSomething(*it) delete *it; it = vpi.erase(it);
does the same thing.
– NathanOliver
Feb 11 at 14:16
1
This solution is O(N^2) (think about the elements which need to be moved when you erase in the middle). Usestd::partition
followed by a singleerase
instead.
– rustyx
Feb 11 at 14:19
4
Consider a vector with 1000 elements, of which first 100 need to be removed. How many times an element that remains would need to be moved inside the vector (around 95000).
– rustyx
Feb 11 at 14:27
|
show 6 more comments
Some approach that I would use:
for (auto i = vector.begin(); i != vector.end(); ++i)
if (SomeTest(*i))
DoSomething(*i);
delete *i;
*i = nullptr;
vector.erase(std::remove(vector.begin(), vector.end(), nullptr), vector.end());
4
this solution is using nullptr as a magic value.
– UmNyobe
Feb 11 at 14:17
That's more or less what I'm doing now
– Jabberwocky
Feb 11 at 14:39
1
@UmNyobe What is wrong with usingnullptr
as a magic value?
– Galik
Feb 11 at 14:43
4
@Galik maybe the vector does containnullptr
and maybeSomeTest
returnsfalse
for anullptr
– user463035818
Feb 11 at 14:44
add a comment |
Keep it simple. YAGNI. No reason to solve a more generic and complicated version of the problem in the off chance you will need it (hint: you won't), or hunt for obscure STL methods (well, unless you wish to).
size_t target = 0;
for (size_t idx = 0; idx < v.size(); idx++)
if (should_delete(v[idx]))
delete v[idx];
else
v[target++] = v[idx];
v.resize(target);
<algorithm>
is not a bunch of "obscure STL methods". Learn them, use them. They produce easy to read, easy to maintain, easy to optimise code.
– YSC
Feb 12 at 7:14
Of course, "easy" is in the eye of the beholder, but I've seen too many code with multiple levels of templatized inheritance which could have been written in a simple for loop or switch statement, only if the programmer resisted the urge to generalize everything to the maximum. If anyone findsstd::for_each
with lambdas easier thanfor
, then it's their taste: but I doubt it will be faster.
– jick
Feb 12 at 18:19
"Of course, "easy" is in the eye of the beholder" No it is generally not. If an idiom exist in the C++ community, anything other than that idiom is not easy to read and increments the wtf/line of your codebase. About optimization, nothing can be said without testing, but generally idiomatic code and code relying on the standard library are better understood by the compiler and optimized in a better way.
– YSC
Feb 12 at 18:25
...Easy is not in the eye of the beholder? I'm glad you agree thatfor
loop is objectively superior! :P Jokes aside, afor
loop has been the idiom of C++ since it was called "C with classes", and unlike C-style strings, it's not going anywhere. Stroustrup himself warned against "trying to write C++ as if it's $(other languages)" in his "The C++ Programming Language" (2nd Ed): I'm not sure if he changed stances later, but C++ is not exactly a functional language, and trying to write C++ as if it's one can easily end up in an overcomplicated mess.
– jick
Feb 12 at 18:33
There's probably no harm in changing a singlefor
loop tofor_each
, but it's silly to claim the latter is objectively superior just because it's new. C++ has no shortage of features that scream "Just because you can use it here doesn't necessarily mean it's a good idea to."
– jick
Feb 12 at 18:33
|
show 1 more comment
Your Answer
StackExchange.ifUsing("editor", function ()
StackExchange.using("externalEditor", function ()
StackExchange.using("snippets", function ()
StackExchange.snippets.init();
);
);
, "code-snippets");
StackExchange.ready(function()
var channelOptions =
tags: "".split(" "),
id: "1"
;
initTagRenderer("".split(" "), "".split(" "), channelOptions);
StackExchange.using("externalEditor", function()
// Have to fire editor after snippets, if snippets enabled
if (StackExchange.settings.snippets.snippetsEnabled)
StackExchange.using("snippets", function()
createEditor();
);
else
createEditor();
);
function createEditor()
StackExchange.prepareEditor(
heartbeatType: 'answer',
autoActivateHeartbeat: false,
convertImagesToLinks: true,
noModals: true,
showLowRepImageUploadWarning: true,
reputationToPostImages: 10,
bindNavPrevention: true,
postfix: "",
imageUploader:
brandingHtml: "Powered by u003ca class="icon-imgur-white" href="https://imgur.com/"u003eu003c/au003e",
contentPolicyHtml: "User contributions licensed under u003ca href="https://creativecommons.org/licenses/by-sa/3.0/"u003ecc by-sa 3.0 with attribution requiredu003c/au003e u003ca href="https://stackoverflow.com/legal/content-policy"u003e(content policy)u003c/au003e",
allowUrls: true
,
onDemand: true,
discardSelector: ".discard-answer"
,immediatelyShowMarkdownHelp:true
);
);
Sign up or log in
StackExchange.ready(function ()
StackExchange.helpers.onClickDraftSave('#login-link');
);
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
StackExchange.ready(
function ()
StackExchange.openid.initPostLogin('.new-post-login', 'https%3a%2f%2fstackoverflow.com%2fquestions%2f54632242%2fdeleting-raw-pointers-from-stdvector%23new-answer', 'question_page');
);
Post as a guest
Required, but never shown
7 Answers
7
active
oldest
votes
7 Answers
7
active
oldest
votes
active
oldest
votes
active
oldest
votes
As often the answer is: know your <algorithm>
s (and is a good reminder to myself) ;)
std::partition
is what you're looking for: std::partition(begin, end, p)
"moves" the elements of the range [begin
, end
) which do not satisfy the predicate p
at the end of the range; you can then treat them as a batch.
auto const to_be_removed = std::partition(begin(v), end(v), (auto p) /* predicate */ );
std::for_each(to_be_removed, end(v), (auto p)
/* crunch */
delete p;
);
v.erase(to_be_removed, end(v));
Full program
#include <iostream>
#include <algorithm>
#include <vector>
int main()
std::vector v = new int0, new int1, new int2 ;
// let's delete all even values
auto const to_be_removed = std::partition(begin(v), end(v), (auto p) return *p % 2 != 0; );
std::for_each(to_be_removed, end(v), (auto p)
std::cout << "Deleting value " << *p << "...n";
delete p;
);
v.erase(to_be_removed, end(v));
Live demo
To go further
This implementation has two major drawbacks: the order from the vector is not stable (1), it could be factored into a reusable function (2).
- (1) is solved by
std::stable_partition
. - (2) is not that hard:
template<class InputIt, class UnaryPredicate, class UnaryDeleter>
InputIt delete_if(InputIt begin, InputIt end, UnaryPredicate p, UnaryDeleter d)
auto const to_be_removed = std::stable_partition(begin, end, std::not_fn(p));
std::for_each(to_be_removed, end, [d](auto p) d(p) ; delete p; );
return to_be_removed;
template<class Container, class UnaryPredicate, class UnaryDeleter>
auto delete_if(Container& c, UnaryPredicate p, UnaryDeleter d)
using std::begin, std::end;
return c.erase(delete_if(begin(c), end(c), p, d), end(c));
Usage:
delete_if(v, SomeTest, DoSomething);
Live demo
Also,delete p
should be handled by theUnaryDeleter
, otherwise it can only be used for dynamic pointers.
– user202729
Feb 11 at 16:13
I disagree on that point; my reasoning is that we're solving OP's specific problem: "for each element in the vector I need to do a test and if the test is positive do something with the [raw] pointer, delete it and then remove it from the vector". There's no reason for them to not put thedelete
statement on thedelete_if
function.
– YSC
Feb 11 at 16:24
add a comment |
As often the answer is: know your <algorithm>
s (and is a good reminder to myself) ;)
std::partition
is what you're looking for: std::partition(begin, end, p)
"moves" the elements of the range [begin
, end
) which do not satisfy the predicate p
at the end of the range; you can then treat them as a batch.
auto const to_be_removed = std::partition(begin(v), end(v), (auto p) /* predicate */ );
std::for_each(to_be_removed, end(v), (auto p)
/* crunch */
delete p;
);
v.erase(to_be_removed, end(v));
Full program
#include <iostream>
#include <algorithm>
#include <vector>
int main()
std::vector v = new int0, new int1, new int2 ;
// let's delete all even values
auto const to_be_removed = std::partition(begin(v), end(v), (auto p) return *p % 2 != 0; );
std::for_each(to_be_removed, end(v), (auto p)
std::cout << "Deleting value " << *p << "...n";
delete p;
);
v.erase(to_be_removed, end(v));
Live demo
To go further
This implementation has two major drawbacks: the order from the vector is not stable (1), it could be factored into a reusable function (2).
- (1) is solved by
std::stable_partition
. - (2) is not that hard:
template<class InputIt, class UnaryPredicate, class UnaryDeleter>
InputIt delete_if(InputIt begin, InputIt end, UnaryPredicate p, UnaryDeleter d)
auto const to_be_removed = std::stable_partition(begin, end, std::not_fn(p));
std::for_each(to_be_removed, end, [d](auto p) d(p) ; delete p; );
return to_be_removed;
template<class Container, class UnaryPredicate, class UnaryDeleter>
auto delete_if(Container& c, UnaryPredicate p, UnaryDeleter d)
using std::begin, std::end;
return c.erase(delete_if(begin(c), end(c), p, d), end(c));
Usage:
delete_if(v, SomeTest, DoSomething);
Live demo
Also,delete p
should be handled by theUnaryDeleter
, otherwise it can only be used for dynamic pointers.
– user202729
Feb 11 at 16:13
I disagree on that point; my reasoning is that we're solving OP's specific problem: "for each element in the vector I need to do a test and if the test is positive do something with the [raw] pointer, delete it and then remove it from the vector". There's no reason for them to not put thedelete
statement on thedelete_if
function.
– YSC
Feb 11 at 16:24
add a comment |
As often the answer is: know your <algorithm>
s (and is a good reminder to myself) ;)
std::partition
is what you're looking for: std::partition(begin, end, p)
"moves" the elements of the range [begin
, end
) which do not satisfy the predicate p
at the end of the range; you can then treat them as a batch.
auto const to_be_removed = std::partition(begin(v), end(v), (auto p) /* predicate */ );
std::for_each(to_be_removed, end(v), (auto p)
/* crunch */
delete p;
);
v.erase(to_be_removed, end(v));
Full program
#include <iostream>
#include <algorithm>
#include <vector>
int main()
std::vector v = new int0, new int1, new int2 ;
// let's delete all even values
auto const to_be_removed = std::partition(begin(v), end(v), (auto p) return *p % 2 != 0; );
std::for_each(to_be_removed, end(v), (auto p)
std::cout << "Deleting value " << *p << "...n";
delete p;
);
v.erase(to_be_removed, end(v));
Live demo
To go further
This implementation has two major drawbacks: the order from the vector is not stable (1), it could be factored into a reusable function (2).
- (1) is solved by
std::stable_partition
. - (2) is not that hard:
template<class InputIt, class UnaryPredicate, class UnaryDeleter>
InputIt delete_if(InputIt begin, InputIt end, UnaryPredicate p, UnaryDeleter d)
auto const to_be_removed = std::stable_partition(begin, end, std::not_fn(p));
std::for_each(to_be_removed, end, [d](auto p) d(p) ; delete p; );
return to_be_removed;
template<class Container, class UnaryPredicate, class UnaryDeleter>
auto delete_if(Container& c, UnaryPredicate p, UnaryDeleter d)
using std::begin, std::end;
return c.erase(delete_if(begin(c), end(c), p, d), end(c));
Usage:
delete_if(v, SomeTest, DoSomething);
Live demo
As often the answer is: know your <algorithm>
s (and is a good reminder to myself) ;)
std::partition
is what you're looking for: std::partition(begin, end, p)
"moves" the elements of the range [begin
, end
) which do not satisfy the predicate p
at the end of the range; you can then treat them as a batch.
auto const to_be_removed = std::partition(begin(v), end(v), (auto p) /* predicate */ );
std::for_each(to_be_removed, end(v), (auto p)
/* crunch */
delete p;
);
v.erase(to_be_removed, end(v));
Full program
#include <iostream>
#include <algorithm>
#include <vector>
int main()
std::vector v = new int0, new int1, new int2 ;
// let's delete all even values
auto const to_be_removed = std::partition(begin(v), end(v), (auto p) return *p % 2 != 0; );
std::for_each(to_be_removed, end(v), (auto p)
std::cout << "Deleting value " << *p << "...n";
delete p;
);
v.erase(to_be_removed, end(v));
Live demo
To go further
This implementation has two major drawbacks: the order from the vector is not stable (1), it could be factored into a reusable function (2).
- (1) is solved by
std::stable_partition
. - (2) is not that hard:
template<class InputIt, class UnaryPredicate, class UnaryDeleter>
InputIt delete_if(InputIt begin, InputIt end, UnaryPredicate p, UnaryDeleter d)
auto const to_be_removed = std::stable_partition(begin, end, std::not_fn(p));
std::for_each(to_be_removed, end, [d](auto p) d(p) ; delete p; );
return to_be_removed;
template<class Container, class UnaryPredicate, class UnaryDeleter>
auto delete_if(Container& c, UnaryPredicate p, UnaryDeleter d)
using std::begin, std::end;
return c.erase(delete_if(begin(c), end(c), p, d), end(c));
Usage:
delete_if(v, SomeTest, DoSomething);
Live demo
edited Feb 14 at 7:24
answered Feb 11 at 14:06
YSCYSC
24.5k555111
24.5k555111
Also,delete p
should be handled by theUnaryDeleter
, otherwise it can only be used for dynamic pointers.
– user202729
Feb 11 at 16:13
I disagree on that point; my reasoning is that we're solving OP's specific problem: "for each element in the vector I need to do a test and if the test is positive do something with the [raw] pointer, delete it and then remove it from the vector". There's no reason for them to not put thedelete
statement on thedelete_if
function.
– YSC
Feb 11 at 16:24
add a comment |
Also,delete p
should be handled by theUnaryDeleter
, otherwise it can only be used for dynamic pointers.
– user202729
Feb 11 at 16:13
I disagree on that point; my reasoning is that we're solving OP's specific problem: "for each element in the vector I need to do a test and if the test is positive do something with the [raw] pointer, delete it and then remove it from the vector". There's no reason for them to not put thedelete
statement on thedelete_if
function.
– YSC
Feb 11 at 16:24
Also,
delete p
should be handled by the UnaryDeleter
, otherwise it can only be used for dynamic pointers.– user202729
Feb 11 at 16:13
Also,
delete p
should be handled by the UnaryDeleter
, otherwise it can only be used for dynamic pointers.– user202729
Feb 11 at 16:13
I disagree on that point; my reasoning is that we're solving OP's specific problem: "for each element in the vector I need to do a test and if the test is positive do something with the [raw] pointer, delete it and then remove it from the vector". There's no reason for them to not put the
delete
statement on the delete_if
function.– YSC
Feb 11 at 16:24
I disagree on that point; my reasoning is that we're solving OP's specific problem: "for each element in the vector I need to do a test and if the test is positive do something with the [raw] pointer, delete it and then remove it from the vector". There's no reason for them to not put the
delete
statement on the delete_if
function.– YSC
Feb 11 at 16:24
add a comment |
You can use std::remove_if
I am not sure why the article you linked uses std::remove_if
before deleting the pointers because that won't work. You have to delete the pointers before the removal:
std::vector<int*> v;
v.erase(std::remove_if(std::begin(v), std::end(v), (int* p)
// do your test and do not remove on failure
if(!SomeTest(p))
return false; // keep this one
DoSomething(p);
// Now we remove but be sure to delete here, before the
// element is moved (and therefore invalidated)
delete p;
return true; // signal for removal
), std::end(v));
Notes: Why this is safe.
Deleting the pointer does not modify the pointer itself, but the object being pointed to. That means that no elements are modified with this approach.
The standard at C++17 28.6.8 5
guarantees that the predicate will be called just once for each element.
Responding to your comment, yes: that's a good idea for this specific case. (+1)
– YSC
Feb 11 at 14:53
add a comment |
You can use std::remove_if
I am not sure why the article you linked uses std::remove_if
before deleting the pointers because that won't work. You have to delete the pointers before the removal:
std::vector<int*> v;
v.erase(std::remove_if(std::begin(v), std::end(v), (int* p)
// do your test and do not remove on failure
if(!SomeTest(p))
return false; // keep this one
DoSomething(p);
// Now we remove but be sure to delete here, before the
// element is moved (and therefore invalidated)
delete p;
return true; // signal for removal
), std::end(v));
Notes: Why this is safe.
Deleting the pointer does not modify the pointer itself, but the object being pointed to. That means that no elements are modified with this approach.
The standard at C++17 28.6.8 5
guarantees that the predicate will be called just once for each element.
Responding to your comment, yes: that's a good idea for this specific case. (+1)
– YSC
Feb 11 at 14:53
add a comment |
You can use std::remove_if
I am not sure why the article you linked uses std::remove_if
before deleting the pointers because that won't work. You have to delete the pointers before the removal:
std::vector<int*> v;
v.erase(std::remove_if(std::begin(v), std::end(v), (int* p)
// do your test and do not remove on failure
if(!SomeTest(p))
return false; // keep this one
DoSomething(p);
// Now we remove but be sure to delete here, before the
// element is moved (and therefore invalidated)
delete p;
return true; // signal for removal
), std::end(v));
Notes: Why this is safe.
Deleting the pointer does not modify the pointer itself, but the object being pointed to. That means that no elements are modified with this approach.
The standard at C++17 28.6.8 5
guarantees that the predicate will be called just once for each element.
You can use std::remove_if
I am not sure why the article you linked uses std::remove_if
before deleting the pointers because that won't work. You have to delete the pointers before the removal:
std::vector<int*> v;
v.erase(std::remove_if(std::begin(v), std::end(v), (int* p)
// do your test and do not remove on failure
if(!SomeTest(p))
return false; // keep this one
DoSomething(p);
// Now we remove but be sure to delete here, before the
// element is moved (and therefore invalidated)
delete p;
return true; // signal for removal
), std::end(v));
Notes: Why this is safe.
Deleting the pointer does not modify the pointer itself, but the object being pointed to. That means that no elements are modified with this approach.
The standard at C++17 28.6.8 5
guarantees that the predicate will be called just once for each element.
edited Feb 11 at 17:22
answered Feb 11 at 14:21
GalikGalik
34.6k35281
34.6k35281
Responding to your comment, yes: that's a good idea for this specific case. (+1)
– YSC
Feb 11 at 14:53
add a comment |
Responding to your comment, yes: that's a good idea for this specific case. (+1)
– YSC
Feb 11 at 14:53
Responding to your comment, yes: that's a good idea for this specific case. (+1)
– YSC
Feb 11 at 14:53
Responding to your comment, yes: that's a good idea for this specific case. (+1)
– YSC
Feb 11 at 14:53
add a comment |
The simplest solution - starting from the linked article - is to take the erase_if
function
template <typename Container, typename Pred>
void erase_if(Container &c, Pred p)
c.erase(std::remove_if(std::begin(c), std::end(c), p), std::end(c));
and just call it with
erase_if(v, (T *pointer)
if (SomeTest(pointer))
DoSomething(pointer);
delete pointer;
return true; //remove pointer from vector
return false;
);
You can obviously split your predicate in two if you want to separate the SomeTest/DoSomething part from the delete
part:
template <typename Container, typename Pred>
void delete_if(Container &c, Pred p)
auto e = std::remove_if(std::begin(c), std::end(c),
[&p](Container::value_type *pointer)
if (p(pointer))
delete pointer;
return true;
return false;
);
c.erase(e, std::end(c));
Since you haven't said why you don't like the erase_if
you linked yourself, I can't guess whether this has the same problem.
1
There's an issue with this approach:std::erase_if(...)
is equivalent tov.erase(std::remove_if(...))
, which explicitly specifies that the predicate must not modify the element.
– You
Feb 11 at 15:37
@You Technically it doesn't modify the element, but I guess the spirit is thatp
should be idempotent (callable many times without harm), and this is certainly not so in this case as the next call on the deleted pointer will probably have UB.
– Arne Vogel
Feb 11 at 15:44
Fair point. (I believe the exact wording is "The [predicate] shall not apply any non-constant function through the dereferenced iterator.", whichdelete
ing technically doesn't do I guess since it's fine todelete
a const pointer.)
– You
Feb 11 at 15:49
@ArneVogel The standard guarantees the predicate will only be called once per element
– Galik
Feb 11 at 17:12
I assume you are deducing this from (alg.remove/5) – IMO it's still a bit of a technicality. If I was doing a code review, I'd suggest switching topartition
/stable_partition
or writing a meaningful comment for why it is correct.
– Arne Vogel
Feb 11 at 20:44
add a comment |
The simplest solution - starting from the linked article - is to take the erase_if
function
template <typename Container, typename Pred>
void erase_if(Container &c, Pred p)
c.erase(std::remove_if(std::begin(c), std::end(c), p), std::end(c));
and just call it with
erase_if(v, (T *pointer)
if (SomeTest(pointer))
DoSomething(pointer);
delete pointer;
return true; //remove pointer from vector
return false;
);
You can obviously split your predicate in two if you want to separate the SomeTest/DoSomething part from the delete
part:
template <typename Container, typename Pred>
void delete_if(Container &c, Pred p)
auto e = std::remove_if(std::begin(c), std::end(c),
[&p](Container::value_type *pointer)
if (p(pointer))
delete pointer;
return true;
return false;
);
c.erase(e, std::end(c));
Since you haven't said why you don't like the erase_if
you linked yourself, I can't guess whether this has the same problem.
1
There's an issue with this approach:std::erase_if(...)
is equivalent tov.erase(std::remove_if(...))
, which explicitly specifies that the predicate must not modify the element.
– You
Feb 11 at 15:37
@You Technically it doesn't modify the element, but I guess the spirit is thatp
should be idempotent (callable many times without harm), and this is certainly not so in this case as the next call on the deleted pointer will probably have UB.
– Arne Vogel
Feb 11 at 15:44
Fair point. (I believe the exact wording is "The [predicate] shall not apply any non-constant function through the dereferenced iterator.", whichdelete
ing technically doesn't do I guess since it's fine todelete
a const pointer.)
– You
Feb 11 at 15:49
@ArneVogel The standard guarantees the predicate will only be called once per element
– Galik
Feb 11 at 17:12
I assume you are deducing this from (alg.remove/5) – IMO it's still a bit of a technicality. If I was doing a code review, I'd suggest switching topartition
/stable_partition
or writing a meaningful comment for why it is correct.
– Arne Vogel
Feb 11 at 20:44
add a comment |
The simplest solution - starting from the linked article - is to take the erase_if
function
template <typename Container, typename Pred>
void erase_if(Container &c, Pred p)
c.erase(std::remove_if(std::begin(c), std::end(c), p), std::end(c));
and just call it with
erase_if(v, (T *pointer)
if (SomeTest(pointer))
DoSomething(pointer);
delete pointer;
return true; //remove pointer from vector
return false;
);
You can obviously split your predicate in two if you want to separate the SomeTest/DoSomething part from the delete
part:
template <typename Container, typename Pred>
void delete_if(Container &c, Pred p)
auto e = std::remove_if(std::begin(c), std::end(c),
[&p](Container::value_type *pointer)
if (p(pointer))
delete pointer;
return true;
return false;
);
c.erase(e, std::end(c));
Since you haven't said why you don't like the erase_if
you linked yourself, I can't guess whether this has the same problem.
The simplest solution - starting from the linked article - is to take the erase_if
function
template <typename Container, typename Pred>
void erase_if(Container &c, Pred p)
c.erase(std::remove_if(std::begin(c), std::end(c), p), std::end(c));
and just call it with
erase_if(v, (T *pointer)
if (SomeTest(pointer))
DoSomething(pointer);
delete pointer;
return true; //remove pointer from vector
return false;
);
You can obviously split your predicate in two if you want to separate the SomeTest/DoSomething part from the delete
part:
template <typename Container, typename Pred>
void delete_if(Container &c, Pred p)
auto e = std::remove_if(std::begin(c), std::end(c),
[&p](Container::value_type *pointer)
if (p(pointer))
delete pointer;
return true;
return false;
);
c.erase(e, std::end(c));
Since you haven't said why you don't like the erase_if
you linked yourself, I can't guess whether this has the same problem.
edited Feb 11 at 15:45
Arne Vogel
4,81521326
4,81521326
answered Feb 11 at 14:17
UselessUseless
44.2k55495
44.2k55495
1
There's an issue with this approach:std::erase_if(...)
is equivalent tov.erase(std::remove_if(...))
, which explicitly specifies that the predicate must not modify the element.
– You
Feb 11 at 15:37
@You Technically it doesn't modify the element, but I guess the spirit is thatp
should be idempotent (callable many times without harm), and this is certainly not so in this case as the next call on the deleted pointer will probably have UB.
– Arne Vogel
Feb 11 at 15:44
Fair point. (I believe the exact wording is "The [predicate] shall not apply any non-constant function through the dereferenced iterator.", whichdelete
ing technically doesn't do I guess since it's fine todelete
a const pointer.)
– You
Feb 11 at 15:49
@ArneVogel The standard guarantees the predicate will only be called once per element
– Galik
Feb 11 at 17:12
I assume you are deducing this from (alg.remove/5) – IMO it's still a bit of a technicality. If I was doing a code review, I'd suggest switching topartition
/stable_partition
or writing a meaningful comment for why it is correct.
– Arne Vogel
Feb 11 at 20:44
add a comment |
1
There's an issue with this approach:std::erase_if(...)
is equivalent tov.erase(std::remove_if(...))
, which explicitly specifies that the predicate must not modify the element.
– You
Feb 11 at 15:37
@You Technically it doesn't modify the element, but I guess the spirit is thatp
should be idempotent (callable many times without harm), and this is certainly not so in this case as the next call on the deleted pointer will probably have UB.
– Arne Vogel
Feb 11 at 15:44
Fair point. (I believe the exact wording is "The [predicate] shall not apply any non-constant function through the dereferenced iterator.", whichdelete
ing technically doesn't do I guess since it's fine todelete
a const pointer.)
– You
Feb 11 at 15:49
@ArneVogel The standard guarantees the predicate will only be called once per element
– Galik
Feb 11 at 17:12
I assume you are deducing this from (alg.remove/5) – IMO it's still a bit of a technicality. If I was doing a code review, I'd suggest switching topartition
/stable_partition
or writing a meaningful comment for why it is correct.
– Arne Vogel
Feb 11 at 20:44
1
1
There's an issue with this approach:
std::erase_if(...)
is equivalent to v.erase(std::remove_if(...))
, which explicitly specifies that the predicate must not modify the element.– You
Feb 11 at 15:37
There's an issue with this approach:
std::erase_if(...)
is equivalent to v.erase(std::remove_if(...))
, which explicitly specifies that the predicate must not modify the element.– You
Feb 11 at 15:37
@You Technically it doesn't modify the element, but I guess the spirit is that
p
should be idempotent (callable many times without harm), and this is certainly not so in this case as the next call on the deleted pointer will probably have UB.– Arne Vogel
Feb 11 at 15:44
@You Technically it doesn't modify the element, but I guess the spirit is that
p
should be idempotent (callable many times without harm), and this is certainly not so in this case as the next call on the deleted pointer will probably have UB.– Arne Vogel
Feb 11 at 15:44
Fair point. (I believe the exact wording is "The [predicate] shall not apply any non-constant function through the dereferenced iterator.", which
delete
ing technically doesn't do I guess since it's fine to delete
a const pointer.)– You
Feb 11 at 15:49
Fair point. (I believe the exact wording is "The [predicate] shall not apply any non-constant function through the dereferenced iterator.", which
delete
ing technically doesn't do I guess since it's fine to delete
a const pointer.)– You
Feb 11 at 15:49
@ArneVogel The standard guarantees the predicate will only be called once per element
– Galik
Feb 11 at 17:12
@ArneVogel The standard guarantees the predicate will only be called once per element
– Galik
Feb 11 at 17:12
I assume you are deducing this from (alg.remove/5) – IMO it's still a bit of a technicality. If I was doing a code review, I'd suggest switching to
partition
/stable_partition
or writing a meaningful comment for why it is correct.– Arne Vogel
Feb 11 at 20:44
I assume you are deducing this from (alg.remove/5) – IMO it's still a bit of a technicality. If I was doing a code review, I'd suggest switching to
partition
/stable_partition
or writing a meaningful comment for why it is correct.– Arne Vogel
Feb 11 at 20:44
add a comment |
With the following approach first you split the elements to be deleted, then you delete, then you adjust the vector.
auto badIt = std::stable_partition(std::beging(v), std::end(v), SomeTestInverse);
std::for_each(badIt, std::end(v), (auto e) DoSomething(e); delete e;);
v.erase(badIt,std::end(v));
The predicate provided must be true for element to keep in order to work, as elements failing the predicate are in the last range.
add a comment |
With the following approach first you split the elements to be deleted, then you delete, then you adjust the vector.
auto badIt = std::stable_partition(std::beging(v), std::end(v), SomeTestInverse);
std::for_each(badIt, std::end(v), (auto e) DoSomething(e); delete e;);
v.erase(badIt,std::end(v));
The predicate provided must be true for element to keep in order to work, as elements failing the predicate are in the last range.
add a comment |
With the following approach first you split the elements to be deleted, then you delete, then you adjust the vector.
auto badIt = std::stable_partition(std::beging(v), std::end(v), SomeTestInverse);
std::for_each(badIt, std::end(v), (auto e) DoSomething(e); delete e;);
v.erase(badIt,std::end(v));
The predicate provided must be true for element to keep in order to work, as elements failing the predicate are in the last range.
With the following approach first you split the elements to be deleted, then you delete, then you adjust the vector.
auto badIt = std::stable_partition(std::beging(v), std::end(v), SomeTestInverse);
std::for_each(badIt, std::end(v), (auto e) DoSomething(e); delete e;);
v.erase(badIt,std::end(v));
The predicate provided must be true for element to keep in order to work, as elements failing the predicate are in the last range.
edited Feb 11 at 14:34
answered Feb 11 at 14:27
UmNyobeUmNyobe
17.8k73875
17.8k73875
add a comment |
add a comment |
Assumed you have a vector of int pointer. Here is my solution:
vector<int*> vpi;
for (vector<int*>::iterator it = vpi.begin(); it != vpi.end(); )
if (SomeTest(*it))
DoSomething(*it)
int* old = *it;
it = vpi.erase(it);
delete old;
else
it++;
3
not sure but as I understood the question this is the code that OP would like to avoid writing.
– user463035818
Feb 11 at 14:10
@LocTran I usually doit = vector.erase(it)
.. not so nice as your old, new :)
– Samer Tufail
Feb 11 at 14:15
3
Why useold
?DoSomething(*it) delete *it; it = vpi.erase(it);
does the same thing.
– NathanOliver
Feb 11 at 14:16
1
This solution is O(N^2) (think about the elements which need to be moved when you erase in the middle). Usestd::partition
followed by a singleerase
instead.
– rustyx
Feb 11 at 14:19
4
Consider a vector with 1000 elements, of which first 100 need to be removed. How many times an element that remains would need to be moved inside the vector (around 95000).
– rustyx
Feb 11 at 14:27
|
show 6 more comments
Assumed you have a vector of int pointer. Here is my solution:
vector<int*> vpi;
for (vector<int*>::iterator it = vpi.begin(); it != vpi.end(); )
if (SomeTest(*it))
DoSomething(*it)
int* old = *it;
it = vpi.erase(it);
delete old;
else
it++;
3
not sure but as I understood the question this is the code that OP would like to avoid writing.
– user463035818
Feb 11 at 14:10
@LocTran I usually doit = vector.erase(it)
.. not so nice as your old, new :)
– Samer Tufail
Feb 11 at 14:15
3
Why useold
?DoSomething(*it) delete *it; it = vpi.erase(it);
does the same thing.
– NathanOliver
Feb 11 at 14:16
1
This solution is O(N^2) (think about the elements which need to be moved when you erase in the middle). Usestd::partition
followed by a singleerase
instead.
– rustyx
Feb 11 at 14:19
4
Consider a vector with 1000 elements, of which first 100 need to be removed. How many times an element that remains would need to be moved inside the vector (around 95000).
– rustyx
Feb 11 at 14:27
|
show 6 more comments
Assumed you have a vector of int pointer. Here is my solution:
vector<int*> vpi;
for (vector<int*>::iterator it = vpi.begin(); it != vpi.end(); )
if (SomeTest(*it))
DoSomething(*it)
int* old = *it;
it = vpi.erase(it);
delete old;
else
it++;
Assumed you have a vector of int pointer. Here is my solution:
vector<int*> vpi;
for (vector<int*>::iterator it = vpi.begin(); it != vpi.end(); )
if (SomeTest(*it))
DoSomething(*it)
int* old = *it;
it = vpi.erase(it);
delete old;
else
it++;
edited Feb 11 at 14:09
answered Feb 11 at 14:07
Loc TranLoc Tran
1,142714
1,142714
3
not sure but as I understood the question this is the code that OP would like to avoid writing.
– user463035818
Feb 11 at 14:10
@LocTran I usually doit = vector.erase(it)
.. not so nice as your old, new :)
– Samer Tufail
Feb 11 at 14:15
3
Why useold
?DoSomething(*it) delete *it; it = vpi.erase(it);
does the same thing.
– NathanOliver
Feb 11 at 14:16
1
This solution is O(N^2) (think about the elements which need to be moved when you erase in the middle). Usestd::partition
followed by a singleerase
instead.
– rustyx
Feb 11 at 14:19
4
Consider a vector with 1000 elements, of which first 100 need to be removed. How many times an element that remains would need to be moved inside the vector (around 95000).
– rustyx
Feb 11 at 14:27
|
show 6 more comments
3
not sure but as I understood the question this is the code that OP would like to avoid writing.
– user463035818
Feb 11 at 14:10
@LocTran I usually doit = vector.erase(it)
.. not so nice as your old, new :)
– Samer Tufail
Feb 11 at 14:15
3
Why useold
?DoSomething(*it) delete *it; it = vpi.erase(it);
does the same thing.
– NathanOliver
Feb 11 at 14:16
1
This solution is O(N^2) (think about the elements which need to be moved when you erase in the middle). Usestd::partition
followed by a singleerase
instead.
– rustyx
Feb 11 at 14:19
4
Consider a vector with 1000 elements, of which first 100 need to be removed. How many times an element that remains would need to be moved inside the vector (around 95000).
– rustyx
Feb 11 at 14:27
3
3
not sure but as I understood the question this is the code that OP would like to avoid writing.
– user463035818
Feb 11 at 14:10
not sure but as I understood the question this is the code that OP would like to avoid writing.
– user463035818
Feb 11 at 14:10
@LocTran I usually do
it = vector.erase(it)
.. not so nice as your old, new :)– Samer Tufail
Feb 11 at 14:15
@LocTran I usually do
it = vector.erase(it)
.. not so nice as your old, new :)– Samer Tufail
Feb 11 at 14:15
3
3
Why use
old
? DoSomething(*it) delete *it; it = vpi.erase(it);
does the same thing.– NathanOliver
Feb 11 at 14:16
Why use
old
? DoSomething(*it) delete *it; it = vpi.erase(it);
does the same thing.– NathanOliver
Feb 11 at 14:16
1
1
This solution is O(N^2) (think about the elements which need to be moved when you erase in the middle). Use
std::partition
followed by a single erase
instead.– rustyx
Feb 11 at 14:19
This solution is O(N^2) (think about the elements which need to be moved when you erase in the middle). Use
std::partition
followed by a single erase
instead.– rustyx
Feb 11 at 14:19
4
4
Consider a vector with 1000 elements, of which first 100 need to be removed. How many times an element that remains would need to be moved inside the vector (around 95000).
– rustyx
Feb 11 at 14:27
Consider a vector with 1000 elements, of which first 100 need to be removed. How many times an element that remains would need to be moved inside the vector (around 95000).
– rustyx
Feb 11 at 14:27
|
show 6 more comments
Some approach that I would use:
for (auto i = vector.begin(); i != vector.end(); ++i)
if (SomeTest(*i))
DoSomething(*i);
delete *i;
*i = nullptr;
vector.erase(std::remove(vector.begin(), vector.end(), nullptr), vector.end());
4
this solution is using nullptr as a magic value.
– UmNyobe
Feb 11 at 14:17
That's more or less what I'm doing now
– Jabberwocky
Feb 11 at 14:39
1
@UmNyobe What is wrong with usingnullptr
as a magic value?
– Galik
Feb 11 at 14:43
4
@Galik maybe the vector does containnullptr
and maybeSomeTest
returnsfalse
for anullptr
– user463035818
Feb 11 at 14:44
add a comment |
Some approach that I would use:
for (auto i = vector.begin(); i != vector.end(); ++i)
if (SomeTest(*i))
DoSomething(*i);
delete *i;
*i = nullptr;
vector.erase(std::remove(vector.begin(), vector.end(), nullptr), vector.end());
4
this solution is using nullptr as a magic value.
– UmNyobe
Feb 11 at 14:17
That's more or less what I'm doing now
– Jabberwocky
Feb 11 at 14:39
1
@UmNyobe What is wrong with usingnullptr
as a magic value?
– Galik
Feb 11 at 14:43
4
@Galik maybe the vector does containnullptr
and maybeSomeTest
returnsfalse
for anullptr
– user463035818
Feb 11 at 14:44
add a comment |
Some approach that I would use:
for (auto i = vector.begin(); i != vector.end(); ++i)
if (SomeTest(*i))
DoSomething(*i);
delete *i;
*i = nullptr;
vector.erase(std::remove(vector.begin(), vector.end(), nullptr), vector.end());
Some approach that I would use:
for (auto i = vector.begin(); i != vector.end(); ++i)
if (SomeTest(*i))
DoSomething(*i);
delete *i;
*i = nullptr;
vector.erase(std::remove(vector.begin(), vector.end(), nullptr), vector.end());
answered Feb 11 at 14:11
S.M.S.M.
6,18641628
6,18641628
4
this solution is using nullptr as a magic value.
– UmNyobe
Feb 11 at 14:17
That's more or less what I'm doing now
– Jabberwocky
Feb 11 at 14:39
1
@UmNyobe What is wrong with usingnullptr
as a magic value?
– Galik
Feb 11 at 14:43
4
@Galik maybe the vector does containnullptr
and maybeSomeTest
returnsfalse
for anullptr
– user463035818
Feb 11 at 14:44
add a comment |
4
this solution is using nullptr as a magic value.
– UmNyobe
Feb 11 at 14:17
That's more or less what I'm doing now
– Jabberwocky
Feb 11 at 14:39
1
@UmNyobe What is wrong with usingnullptr
as a magic value?
– Galik
Feb 11 at 14:43
4
@Galik maybe the vector does containnullptr
and maybeSomeTest
returnsfalse
for anullptr
– user463035818
Feb 11 at 14:44
4
4
this solution is using nullptr as a magic value.
– UmNyobe
Feb 11 at 14:17
this solution is using nullptr as a magic value.
– UmNyobe
Feb 11 at 14:17
That's more or less what I'm doing now
– Jabberwocky
Feb 11 at 14:39
That's more or less what I'm doing now
– Jabberwocky
Feb 11 at 14:39
1
1
@UmNyobe What is wrong with using
nullptr
as a magic value?– Galik
Feb 11 at 14:43
@UmNyobe What is wrong with using
nullptr
as a magic value?– Galik
Feb 11 at 14:43
4
4
@Galik maybe the vector does contain
nullptr
and maybe SomeTest
returns false
for a nullptr
– user463035818
Feb 11 at 14:44
@Galik maybe the vector does contain
nullptr
and maybe SomeTest
returns false
for a nullptr
– user463035818
Feb 11 at 14:44
add a comment |
Keep it simple. YAGNI. No reason to solve a more generic and complicated version of the problem in the off chance you will need it (hint: you won't), or hunt for obscure STL methods (well, unless you wish to).
size_t target = 0;
for (size_t idx = 0; idx < v.size(); idx++)
if (should_delete(v[idx]))
delete v[idx];
else
v[target++] = v[idx];
v.resize(target);
<algorithm>
is not a bunch of "obscure STL methods". Learn them, use them. They produce easy to read, easy to maintain, easy to optimise code.
– YSC
Feb 12 at 7:14
Of course, "easy" is in the eye of the beholder, but I've seen too many code with multiple levels of templatized inheritance which could have been written in a simple for loop or switch statement, only if the programmer resisted the urge to generalize everything to the maximum. If anyone findsstd::for_each
with lambdas easier thanfor
, then it's their taste: but I doubt it will be faster.
– jick
Feb 12 at 18:19
"Of course, "easy" is in the eye of the beholder" No it is generally not. If an idiom exist in the C++ community, anything other than that idiom is not easy to read and increments the wtf/line of your codebase. About optimization, nothing can be said without testing, but generally idiomatic code and code relying on the standard library are better understood by the compiler and optimized in a better way.
– YSC
Feb 12 at 18:25
...Easy is not in the eye of the beholder? I'm glad you agree thatfor
loop is objectively superior! :P Jokes aside, afor
loop has been the idiom of C++ since it was called "C with classes", and unlike C-style strings, it's not going anywhere. Stroustrup himself warned against "trying to write C++ as if it's $(other languages)" in his "The C++ Programming Language" (2nd Ed): I'm not sure if he changed stances later, but C++ is not exactly a functional language, and trying to write C++ as if it's one can easily end up in an overcomplicated mess.
– jick
Feb 12 at 18:33
There's probably no harm in changing a singlefor
loop tofor_each
, but it's silly to claim the latter is objectively superior just because it's new. C++ has no shortage of features that scream "Just because you can use it here doesn't necessarily mean it's a good idea to."
– jick
Feb 12 at 18:33
|
show 1 more comment
Keep it simple. YAGNI. No reason to solve a more generic and complicated version of the problem in the off chance you will need it (hint: you won't), or hunt for obscure STL methods (well, unless you wish to).
size_t target = 0;
for (size_t idx = 0; idx < v.size(); idx++)
if (should_delete(v[idx]))
delete v[idx];
else
v[target++] = v[idx];
v.resize(target);
<algorithm>
is not a bunch of "obscure STL methods". Learn them, use them. They produce easy to read, easy to maintain, easy to optimise code.
– YSC
Feb 12 at 7:14
Of course, "easy" is in the eye of the beholder, but I've seen too many code with multiple levels of templatized inheritance which could have been written in a simple for loop or switch statement, only if the programmer resisted the urge to generalize everything to the maximum. If anyone findsstd::for_each
with lambdas easier thanfor
, then it's their taste: but I doubt it will be faster.
– jick
Feb 12 at 18:19
"Of course, "easy" is in the eye of the beholder" No it is generally not. If an idiom exist in the C++ community, anything other than that idiom is not easy to read and increments the wtf/line of your codebase. About optimization, nothing can be said without testing, but generally idiomatic code and code relying on the standard library are better understood by the compiler and optimized in a better way.
– YSC
Feb 12 at 18:25
...Easy is not in the eye of the beholder? I'm glad you agree thatfor
loop is objectively superior! :P Jokes aside, afor
loop has been the idiom of C++ since it was called "C with classes", and unlike C-style strings, it's not going anywhere. Stroustrup himself warned against "trying to write C++ as if it's $(other languages)" in his "The C++ Programming Language" (2nd Ed): I'm not sure if he changed stances later, but C++ is not exactly a functional language, and trying to write C++ as if it's one can easily end up in an overcomplicated mess.
– jick
Feb 12 at 18:33
There's probably no harm in changing a singlefor
loop tofor_each
, but it's silly to claim the latter is objectively superior just because it's new. C++ has no shortage of features that scream "Just because you can use it here doesn't necessarily mean it's a good idea to."
– jick
Feb 12 at 18:33
|
show 1 more comment
Keep it simple. YAGNI. No reason to solve a more generic and complicated version of the problem in the off chance you will need it (hint: you won't), or hunt for obscure STL methods (well, unless you wish to).
size_t target = 0;
for (size_t idx = 0; idx < v.size(); idx++)
if (should_delete(v[idx]))
delete v[idx];
else
v[target++] = v[idx];
v.resize(target);
Keep it simple. YAGNI. No reason to solve a more generic and complicated version of the problem in the off chance you will need it (hint: you won't), or hunt for obscure STL methods (well, unless you wish to).
size_t target = 0;
for (size_t idx = 0; idx < v.size(); idx++)
if (should_delete(v[idx]))
delete v[idx];
else
v[target++] = v[idx];
v.resize(target);
answered Feb 11 at 18:33
jickjick
28719
28719
<algorithm>
is not a bunch of "obscure STL methods". Learn them, use them. They produce easy to read, easy to maintain, easy to optimise code.
– YSC
Feb 12 at 7:14
Of course, "easy" is in the eye of the beholder, but I've seen too many code with multiple levels of templatized inheritance which could have been written in a simple for loop or switch statement, only if the programmer resisted the urge to generalize everything to the maximum. If anyone findsstd::for_each
with lambdas easier thanfor
, then it's their taste: but I doubt it will be faster.
– jick
Feb 12 at 18:19
"Of course, "easy" is in the eye of the beholder" No it is generally not. If an idiom exist in the C++ community, anything other than that idiom is not easy to read and increments the wtf/line of your codebase. About optimization, nothing can be said without testing, but generally idiomatic code and code relying on the standard library are better understood by the compiler and optimized in a better way.
– YSC
Feb 12 at 18:25
...Easy is not in the eye of the beholder? I'm glad you agree thatfor
loop is objectively superior! :P Jokes aside, afor
loop has been the idiom of C++ since it was called "C with classes", and unlike C-style strings, it's not going anywhere. Stroustrup himself warned against "trying to write C++ as if it's $(other languages)" in his "The C++ Programming Language" (2nd Ed): I'm not sure if he changed stances later, but C++ is not exactly a functional language, and trying to write C++ as if it's one can easily end up in an overcomplicated mess.
– jick
Feb 12 at 18:33
There's probably no harm in changing a singlefor
loop tofor_each
, but it's silly to claim the latter is objectively superior just because it's new. C++ has no shortage of features that scream "Just because you can use it here doesn't necessarily mean it's a good idea to."
– jick
Feb 12 at 18:33
|
show 1 more comment
<algorithm>
is not a bunch of "obscure STL methods". Learn them, use them. They produce easy to read, easy to maintain, easy to optimise code.
– YSC
Feb 12 at 7:14
Of course, "easy" is in the eye of the beholder, but I've seen too many code with multiple levels of templatized inheritance which could have been written in a simple for loop or switch statement, only if the programmer resisted the urge to generalize everything to the maximum. If anyone findsstd::for_each
with lambdas easier thanfor
, then it's their taste: but I doubt it will be faster.
– jick
Feb 12 at 18:19
"Of course, "easy" is in the eye of the beholder" No it is generally not. If an idiom exist in the C++ community, anything other than that idiom is not easy to read and increments the wtf/line of your codebase. About optimization, nothing can be said without testing, but generally idiomatic code and code relying on the standard library are better understood by the compiler and optimized in a better way.
– YSC
Feb 12 at 18:25
...Easy is not in the eye of the beholder? I'm glad you agree thatfor
loop is objectively superior! :P Jokes aside, afor
loop has been the idiom of C++ since it was called "C with classes", and unlike C-style strings, it's not going anywhere. Stroustrup himself warned against "trying to write C++ as if it's $(other languages)" in his "The C++ Programming Language" (2nd Ed): I'm not sure if he changed stances later, but C++ is not exactly a functional language, and trying to write C++ as if it's one can easily end up in an overcomplicated mess.
– jick
Feb 12 at 18:33
There's probably no harm in changing a singlefor
loop tofor_each
, but it's silly to claim the latter is objectively superior just because it's new. C++ has no shortage of features that scream "Just because you can use it here doesn't necessarily mean it's a good idea to."
– jick
Feb 12 at 18:33
<algorithm>
is not a bunch of "obscure STL methods". Learn them, use them. They produce easy to read, easy to maintain, easy to optimise code.– YSC
Feb 12 at 7:14
<algorithm>
is not a bunch of "obscure STL methods". Learn them, use them. They produce easy to read, easy to maintain, easy to optimise code.– YSC
Feb 12 at 7:14
Of course, "easy" is in the eye of the beholder, but I've seen too many code with multiple levels of templatized inheritance which could have been written in a simple for loop or switch statement, only if the programmer resisted the urge to generalize everything to the maximum. If anyone finds
std::for_each
with lambdas easier than for
, then it's their taste: but I doubt it will be faster.– jick
Feb 12 at 18:19
Of course, "easy" is in the eye of the beholder, but I've seen too many code with multiple levels of templatized inheritance which could have been written in a simple for loop or switch statement, only if the programmer resisted the urge to generalize everything to the maximum. If anyone finds
std::for_each
with lambdas easier than for
, then it's their taste: but I doubt it will be faster.– jick
Feb 12 at 18:19
"Of course, "easy" is in the eye of the beholder" No it is generally not. If an idiom exist in the C++ community, anything other than that idiom is not easy to read and increments the wtf/line of your codebase. About optimization, nothing can be said without testing, but generally idiomatic code and code relying on the standard library are better understood by the compiler and optimized in a better way.
– YSC
Feb 12 at 18:25
"Of course, "easy" is in the eye of the beholder" No it is generally not. If an idiom exist in the C++ community, anything other than that idiom is not easy to read and increments the wtf/line of your codebase. About optimization, nothing can be said without testing, but generally idiomatic code and code relying on the standard library are better understood by the compiler and optimized in a better way.
– YSC
Feb 12 at 18:25
...Easy is not in the eye of the beholder? I'm glad you agree that
for
loop is objectively superior! :P Jokes aside, a for
loop has been the idiom of C++ since it was called "C with classes", and unlike C-style strings, it's not going anywhere. Stroustrup himself warned against "trying to write C++ as if it's $(other languages)" in his "The C++ Programming Language" (2nd Ed): I'm not sure if he changed stances later, but C++ is not exactly a functional language, and trying to write C++ as if it's one can easily end up in an overcomplicated mess.– jick
Feb 12 at 18:33
...Easy is not in the eye of the beholder? I'm glad you agree that
for
loop is objectively superior! :P Jokes aside, a for
loop has been the idiom of C++ since it was called "C with classes", and unlike C-style strings, it's not going anywhere. Stroustrup himself warned against "trying to write C++ as if it's $(other languages)" in his "The C++ Programming Language" (2nd Ed): I'm not sure if he changed stances later, but C++ is not exactly a functional language, and trying to write C++ as if it's one can easily end up in an overcomplicated mess.– jick
Feb 12 at 18:33
There's probably no harm in changing a single
for
loop to for_each
, but it's silly to claim the latter is objectively superior just because it's new. C++ has no shortage of features that scream "Just because you can use it here doesn't necessarily mean it's a good idea to."– jick
Feb 12 at 18:33
There's probably no harm in changing a single
for
loop to for_each
, but it's silly to claim the latter is objectively superior just because it's new. C++ has no shortage of features that scream "Just because you can use it here doesn't necessarily mean it's a good idea to."– jick
Feb 12 at 18:33
|
show 1 more comment
Thanks for contributing an answer to Stack Overflow!
- Please be sure to answer the question. Provide details and share your research!
But avoid …
- Asking for help, clarification, or responding to other answers.
- Making statements based on opinion; back them up with references or personal experience.
To learn more, see our tips on writing great answers.
Sign up or log in
StackExchange.ready(function ()
StackExchange.helpers.onClickDraftSave('#login-link');
);
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
StackExchange.ready(
function ()
StackExchange.openid.initPostLogin('.new-post-login', 'https%3a%2f%2fstackoverflow.com%2fquestions%2f54632242%2fdeleting-raw-pointers-from-stdvector%23new-answer', 'question_page');
);
Post as a guest
Required, but never shown
Sign up or log in
StackExchange.ready(function ()
StackExchange.helpers.onClickDraftSave('#login-link');
);
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
Sign up or log in
StackExchange.ready(function ()
StackExchange.helpers.onClickDraftSave('#login-link');
);
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
Sign up or log in
StackExchange.ready(function ()
StackExchange.helpers.onClickDraftSave('#login-link');
);
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
10
sorry, nitpick: raw pointers to objects owned by someone else are rather harmless. Only owning raw pointers are evil
– user463035818
Feb 11 at 14:04
1
The only concern I can think of is invalidating the iterator, and messing up your loop, which wouldn't have anything to do with the pointer. Perhaps you could post a Minimal, Complete, and Verifiable example. (consider std::remove_if ?)
– Kenny Ostrom
Feb 11 at 14:04
5
What specifically is wrong with the erase_if?
– Useless
Feb 11 at 14:07
1
@user463035818 Needless to say,
delete
ing raw pointers only makes sense specifically in the evil case of owning raw pointers. The statement in the question does not explicitly attribute ownership to the pointers so you're technically correct to nitpick.– Max Langhof
Feb 11 at 14:13
4
@MaxLanghof if the vector is responsible to
delete
them then I would say the vector owns them. Anyhow, I was specifically refering to "I know raw pointers are "evil"" and I am a bit picky whem something gets the "evil" attribute. Too easily this can lead to misunderstanding– user463035818
Feb 11 at 14:15