473,404 Members | 2,187 Online
Bytes | Software Development & Data Engineering Community
Post Job

Home Posts Topics Members FAQ

Join Bytes to post your question to a community of 473,404 software developers and data experts.

Is this valid code?

I was having a discussion with a friend about if the following is valid
code. I'm fairly sure it isn't allowed, but my friend seems to think
it's fine. It definatly appears to run fine in all the compilers we can
get access to. Any comments?

Bob

void remove_duplicates(std::list<int>& in_list)
{
std::list<int>::iterator it = in_list.begin();
in_list.remove(*it);
}
Jul 23 '05 #1
12 1580
Bob Brian wrote:
I was having a discussion with a friend about if the following is valid
code. I'm fairly sure it isn't allowed, but my friend seems to think
it's fine. It definatly appears to run fine in all the compilers we can
get access to. Any comments?

Bob

void remove_duplicates(std::list<int>& in_list)
{
std::list<int>::iterator it = in_list.begin();
in_list.remove(*it);
}


Well, why do YOU think it wouldn't compile?

I think it would compile, but it wouldn't do what it's supposed to.
What it does is to remove all elements in in_list which equal the value
of *(in_list.begin()).

On top of that, if in_list is empty, the behavior of your function is
most probably undefined.

Regards,
Matthias
Jul 23 '05 #2
matthias_k wrote:
Bob Brian wrote:
I was having a discussion with a friend about if the following is
valid code. I'm fairly sure it isn't allowed, but my friend seems to
think it's fine. It definatly appears to run fine in all the compilers
we can get access to. Any comments?

Bob

void remove_duplicates(std::list<int>& in_list)
{
std::list<int>::iterator it = in_list.begin();
in_list.remove(*it);
}


Well, why do YOU think it wouldn't compile?

I think it would compile, but it wouldn't do what it's supposed to.
What it does is to remove all elements in in_list which equal the value
of *(in_list.begin()).

On top of that, if in_list is empty, the behavior of your function is
most probably undefined.

Regards,
Matthias


// test.cpp
#include <list>

void remove_duplicates(std::list<int>& in_list)
{
std::list<int>::iterator it = in_list.begin();
in_list.remove(*it);
}

$ g++ -pedantic -Wall -std=c++98 -o test test.cpp
--> no errors.

This may be a hint that this code is at least syntactically correct :)

Regards,
Matthias
Jul 23 '05 #3
matthias_k wrote:
Bob Brian wrote:
I was having a discussion with a friend about if the following is
valid code. I'm fairly sure it isn't allowed, but my friend seems to
think it's fine. It definatly appears to run fine in all the compilers
we can get access to. Any comments?

Bob

void remove_duplicates(std::list<int>& in_list)
{
std::list<int>::iterator it = in_list.begin();
in_list.remove(*it);
}


Well, why do YOU think it wouldn't compile?

I think it would compile, but it wouldn't do what it's supposed to.
What it does is to remove all elements in in_list which equal the value
of *(in_list.begin()).

On top of that, if in_list is empty, the behavior of your function is
most probably undefined.


Sorry, I have just realised I pasted in a bit of code without any
explanation as to the problem!

The code does compile, and apart from the empty list case (woops! forgot
that!) does what it is supposed to do, which is remove all copies of the
first element of the list.

However, it is my belief that this code isn't actually defined, as I
read that in_list.remove is supposed to take *it by reference, and there
is no need for it to make an internal copy. Therefore as soon as the
first element of in_list has been removed then the rest of the remove
function is getting the value of it from freed memory. The code happens
to work because the value of it is generally pulled into a register and
kept there.

My friend believes it is valid, as there isn't a comment in the standard
that you can't call in_list.remove with an element of in_list.

Bob
Jul 23 '05 #4
matthias_k wrote:
What it does is to remove all elements in in_list which equal the value
of *(in_list.begin()).


This should of course read:
.... remove all *duplicates* in in_list which equal the value
of *(in_list.begin()).
Jul 23 '05 #5
Bob Brian wrote:
Bob Brian wrote:

The code does compile, and apart from the empty list case (woops! forgot
that!) does what it is supposed to do, which is remove all copies of the
first element of the list.

However, it is my belief that this code isn't actually defined, as I
read that in_list.remove is supposed to take *it by reference, and there
is no need for it to make an internal copy. Therefore as soon as the
first element of in_list has been removed then the rest of the remove
function is getting the value of it from freed memory. The code happens
to work because the value of it is generally pulled into a register and
kept there.


First of all, it doesn't remove the first element, it removes all
elements which duplicate it (in your case the first one, if it should
happen to exist, but not the element itself).
Furthermore, it takes its argument by const reference, so there is no
way it will ever be overwritten. How should remove() lose the
information which elements to remove?

Honestly, I don't know how remove() internally works. But I think we can
have enough trust in the implementors of std::list that this function
does what it's supposed to =)

Regards,
Matthias
Jul 23 '05 #6
In message <ct*************@news.t-online.com>, matthias_k
<no****@digitalraid.com> writes
Bob Brian wrote:
Bob Brian wrote: The code does compile, and apart from the empty list case (woops!
forgot that!) does what it is supposed to do, which is remove all
copies of the first element of the list.
However, it is my belief that this code isn't actually defined, as I
read that in_list.remove is supposed to take *it by reference, and
there is no need for it to make an internal copy. Therefore as soon
as the first element of in_list has been removed then the rest of the
function is getting the value of it from freed memory. The code
happens to work because the value of it is generally pulled into a
register and kept there.


I tend to agree with this analysis - but it would be more compelling for
a list, not of int, but of something with a destructor.
First of all, it doesn't remove the first element, it removes all
elements which duplicate it (in your case the first one, if it should
happen to exist, but not the element itself).
???

The first element, by definition, is equal to the value passed to
remove(), and will therefore get removed. If it were a user-defined type
rather than int, its destructor would be called. After that, all
attempts to use it are UB.
Furthermore, it takes its argument by const reference, so there is no
way it will ever be overwritten.
That reference is to the first element. What is that a reference to,
once remove() has erased the first element (and maybe called its
destructor)?
How should remove() lose the information which elements to remove?
By carrying around a reference to something which has been deleted.
Honestly, I don't know how remove() internally works. But I think we
can have enough trust in the implementors of std::list that this
function does what it's supposed to =)


Certainly. But are we supposing what they supposed?

--
Richard Herring
Jul 23 '05 #7
Yes you are right of course, my bad. Late afternoon tiredness :D
Jul 23 '05 #8
By the way:

01010 /**
01011 * @brief Remove consecutive duplicate elements.
01012 *
01013 * For each consecutive set of elements with the same value,
01014 * remove all but the first one. Remaining elements stay in
01015 * list order. Note that this function only erases the
01016 * elements, and that if the elements themselves are pointers,
01017 * the pointed-to memory is not touched in any way. Managing
01018 * the pointer is the user's responsibilty.
01019 */
01020 void
01021 unique();

Just use this one :)
Jul 23 '05 #9
In message <ct*************@news.t-online.com>, matthias_k
<no****@digitalraid.com> writes
By the way:

01010 /**
01011 * @brief Remove consecutive duplicate elements.
01012 *
01013 * For each consecutive set of elements with the same value,
01014 * remove all but the first one. Remaining elements stay in
01015 * list order. Note that this function only erases the
01016 * elements, and that if the elements themselves are pointers,
01017 * the pointed-to memory is not touched in any way. Managing
01018 * the pointer is the user's responsibilty.
01019 */
01020 void
01021 unique();

Just use this one :)


Did you miss the word "consecutive" above?

--
Richard Herring
Jul 23 '05 #10
Richard Herring wrote:
Did you miss the word "consecutive" above?


Good point. Nevermind. :D
Jul 23 '05 #11
Richard Herring wrote:
In message <ct*************@news.t-online.com>, matthias_k
<no****@digitalraid.com> writes
Bob Brian wrote:
Bob Brian wrote:

The code does compile, and apart from the empty list case (woops!
forgot that!) does what it is supposed to do, which is remove all
copies of the first element of the list.
However, it is my belief that this code isn't actually defined, as I
read that in_list.remove is supposed to take *it by reference, and
there is no need for it to make an internal copy. Therefore as soon
as the first element of in_list has been removed then the rest of
the function is getting the value of it from freed memory. The code
happens to work because the value of it is generally pulled into a
register and kept there.

I tend to agree with this analysis - but it would be more compelling for
a list, not of int, but of something with a destructor.

First of all, it doesn't remove the first element, it removes all
elements which duplicate it (in your case the first one, if it should
happen to exist, but not the element itself).

???

The first element, by definition, is equal to the value passed to
remove(), and will therefore get removed. If it were a user-defined type
rather than int, its destructor would be called. After that, all
attempts to use it are UB.
Furthermore, it takes its argument by const reference, so there is no
way it will ever be overwritten.

That reference is to the first element. What is that a reference to,
once remove() has erased the first element (and maybe called its
destructor)?
How should remove() lose the information which elements to remove?

By carrying around a reference to something which has been deleted.
Honestly, I don't know how remove() internally works. But I think we
can have enough trust in the implementors of std::list that this
function does what it's supposed to =)

Certainly. But are we supposing what they supposed?


I'm supposing that they don't assume they have to copy the reference
they recieve before they use it, and I'm not sure there is a way to go
from *it and get back to the iterator "it" (well actually in most
implementations there probably is, but the standard doesn't say there
is).. on the other hand it seems like perhaps the standard should say
something like "If any function during it's normal operation would
result in a change to any of its parameters, the results are undefined"
or something?" Or is it just assumed people have to figure that out for
themselves? :)
Jul 23 '05 #12

Bob Brian wrote:
I was having a discussion with a friend about if the following is valid code. I'm fairly sure it isn't allowed, but my friend seems to think
it's fine. It definatly appears to run fine in all the compilers we can get access to. void remove_duplicates(std::list<int>& in_list)
{
std::list<int>::iterator it = in_list.begin();
in_list.remove(*it);
}


Illegal. (and it doesn't do what the name suggests, either).

23.2.2.4/18 makes it clear that operator== is applied repeatedly,
possibly using the reference (not copied). 24.1.3 Table 74 makes
it clear that *it doesn't copy the value_type either. Hence, after
the first removal, the reference becomes unusable but .remove( )
may still assume it's valid.

One fix is obvious: in_list.remove(int(*it));
Just introduce a temporary which does remain valid.
HTH,
Michiel Salters

Jul 23 '05 #13

This thread has been closed and replies have been disabled. Please start a new discussion.

Similar topics

12
by: lawrence | last post by:
I have a string which I want to send to eval(). How can I test it ahead of time to make sure it is valid code? I don't want to send it to eval and get parse errors. I want to do something like...
0
by: Tao | last post by:
I just upgraded .NET framework to 1.1 and VS.Net to 2003 version and tried to test it out. I created an ASP.NET project using the wizard and tried to run it by hitting "F5". I got an exception:...
23
by: James Aguilar | last post by:
Someone showed me something today that I didn't understand. This doesn't seem like it should be valid C++. Specifically, I don't understand how the commas are accepted after the function...
8
by: Pieter | last post by:
Hi, I'm having some weird problem using the BackGroundWorker in an Outlook (2003) Add-In, with VB.NET 2005: I'm using the BackGroundWorker to get the info of some mailitems, and after each item...
8
by: Pieter | last post by:
Hi, I'm having some weird problem using the BackGroundWorker in an Outlook (2003) Add-In, with VB.NET 2005: I'm using the BackGroundWorker to get the info of some mailitems, and after each item...
0
BarryA
by: BarryA | last post by:
What are the essential steps and strategies outlined in the Data Structures and Algorithms (DSA) roadmap for aspiring data scientists? How can individuals effectively utilize this roadmap to progress...
1
by: nemocccc | last post by:
hello, everyone, I want to develop a software for my android phone for daily needs, any suggestions?
1
by: Sonnysonu | last post by:
This is the data of csv file 1 2 3 1 2 3 1 2 3 1 2 3 2 3 2 3 3 the lengths should be different i have to store the data by column-wise with in the specific length. suppose the i have to...
0
by: Hystou | last post by:
There are some requirements for setting up RAID: 1. The motherboard and BIOS support RAID configuration. 2. The motherboard has 2 or more available SATA protocol SSD/HDD slots (including MSATA, M.2...
0
marktang
by: marktang | last post by:
ONU (Optical Network Unit) is one of the key components for providing high-speed Internet services. Its primary function is to act as an endpoint device located at the user's premises. However,...
0
by: Hystou | last post by:
Most computers default to English, but sometimes we require a different language, especially when relocating. Forgot to request a specific language before your computer shipped? No problem! You can...
0
Oralloy
by: Oralloy | last post by:
Hello folks, I am unable to find appropriate documentation on the type promotion of bit-fields when using the generalised comparison operator "<=>". The problem is that using the GNU compilers,...
0
jinu1996
by: jinu1996 | last post by:
In today's digital age, having a compelling online presence is paramount for businesses aiming to thrive in a competitive landscape. At the heart of this digital strategy lies an intricately woven...
0
agi2029
by: agi2029 | last post by:
Let's talk about the concept of autonomous AI software engineers and no-code agents. These AIs are designed to manage the entire lifecycle of a software development project—planning, coding, testing,...

By using Bytes.com and it's services, you agree to our Privacy Policy and Terms of Use.

To disable or enable advertisements and analytics tracking please visit the manage ads & tracking page.