Let start from the description of operators [] of class std::basic_string ([string.access]). The decription points out only one requirement pos <= size.If pos exactly less than size() then operators return *(begin() + pos). Otherwise, they return "a reference to an object of type charT with value charT(), where modifying the object leads to undefined behavior" (quatation of the C++ STandard).So using of operators does not require that an object of std::basic_string will be non-empty.
Now let consider the decription of member functions front(). There is written that effects of their usage is equivalent to operator [](0).What does this mean? it means that if an object is empty the functions will return "a reference to an object of type charT with value charT(), where modifying the object leads to undefined behavior".
It seems that all is o'k. However it is not the truth. The description of member functions front() requires that the condition !empty() were satisfied.
So this codestd:;string s;if ( s[0] ) { /*...*? }is valid while this codestd::string s;if ( s.front() ) { /*...*? }is wrong.The worse situation with member functions back(). They are equivalent to operator[](size() - 1). Take into account the expression in the parentheses.
So I suggest remove the requirement !empty() from the descriptions of front() and back(). This requirement only confuses users and prevents to write generic clear and readable code.operator []( 0 ), front() and back() shall behave yourself the same way when an obejct of type std::basic_string is empty.
Yes, you are right. And there is no any sense to keep this contradiction/ All free kinds of functions, operator []( 0 ), back(), front() shall behave the same way whether a string is empty or no.
On Wednesday, April 17, 2013 10:18:49 PM UTC+4, Nevin ":-)" Liber wrote:On 17 April 2013 19:13, Vlad from Moscow <vlad....@mail.ru> wrote:Yes, you are right. And there is no any sense to keep this contradiction/ All free kinds of functions, operator []( 0 ), back(), front() shall behave the same way whether a string is empty or no.Even if you wish to remove it from front() (and I agree with Ville that we shouldn't for the reasons he stated), you cannot remove it from back(), as the back of a string is not and has never been the nul-terminator.--
Nevin ":-)" Liber <mailto:ne...@eviloverlord.com> (847) 691-1404I am dorry but I do not see arguments. You are saying that back().."is not and has never been the nul-terminator." And what?I think that for an empty string there shall be an invariant: operator [](0 ) == front() == back(). This will allow to write a generic code.Consider a very simple task of finding a pare of adjacent elements of a container of strings where the last character of the previous string shall be equal to the first character of the next string. In fact you are using the conditionprev.back() == next.front()But you may not use this condition.
You can change it the following bwayprev.back() == next[0]
Now the right operand is a valid expresson. But what to do with the left operand?Take into account also that either the ledt operand or the write operand or the both can be empty. So you should make the condition more compound and test each string whether it is empty. Why should you do all this? I do not see any sense. It would be much simpler and ckear to write the original expressionprev.back() == next.front()and do not bother that strings can be empty.
On Wednesday, April 17, 2013 11:50:02 AM UTC-7, Vlad from Moscow wrote:
On Wednesday, April 17, 2013 10:18:49 PM UTC+4, Nevin ":-)" Liber wrote:On 17 April 2013 19:13, Vlad from Moscow <vlad....@mail.ru> wrote:Yes, you are right. And there is no any sense to keep this contradiction/ All free kinds of functions, operator []( 0 ), back(), front() shall behave the same way whether a string is empty or no.Even if you wish to remove it from front() (and I agree with Ville that we shouldn't for the reasons he stated), you cannot remove it from back(), as the back of a string is not and has never been the nul-terminator.--
Nevin ":-)" Liber <mailto:ne...@eviloverlord.com> (847) 691-1404I am dorry but I do not see arguments. You are saying that back().."is not and has never been the nul-terminator." And what?I think that for an empty string there shall be an invariant: operator [](0 ) == front() == back(). This will allow to write a generic code.Consider a very simple task of finding a pare of adjacent elements of a container of strings where the last character of the previous string shall be equal to the first character of the next string. In fact you are using the conditionprev.back() == next.front()But you may not use this condition.
Um, why not? If this container could contain empty strings, then such a test is very much wrong. Not unless you filter out empty strings and do something different for them.You can change it the following bwayprev.back() == next[0]Now the right operand is a valid expresson. But what to do with the left operand?Take into account also that either the ledt operand or the write operand or the both can be empty. So you should make the condition more compound and test each string whether it is empty. Why should you do all this? I do not see any sense. It would be much simpler and ckear to write the original expressionprev.back() == next.front()and do not bother that strings can be empty.
If the code worked the way you wanted, you would the wrong answer. If you had two empty strings next to each other, they would be considered to match this test. That is wrong, because two empty strings are empty; they contain no characters. Therefore, they cannot fit your description: "the last character of the previous string shall be equal to the first character of the next string".
Indeed, this is precisely why we don't do what you're asking for. It would give the false impression that `front` and `back` always return values no matter the size of the string. It would give the false impression that `begin` and `end` always return valid iterators. And so forth. People such as yourself will start treating the NULL-terminator as part of the string. Which it is not.
So if I need to compare the first character with the last character then the expression will look something asif ( s[0] == ( s.size() == 0 ? s[0] : s.back() )But even this expression is not satisfied because according to your wrong logic if size() == 0 then I may not compare operator []( 0 ) with operator {} ( 0 ).Take into account that you even may not to write s.front() instead of s[0] as the left operand of the condition.:)
\The problem is that s[0] and s.front() are not equivalent. I would prefer in many cases to use s.front() instead of s[0] and s.back() instead of !s.empty() && s.back() but these two records are not equivalent. It only confuses users.
You should take into account that std::basic_string is a special type of containers. It simulates character arrays.
Thta it would be more clear let consider a simple task. There is some sequense of words among which empty words can be present and we need to build a second sequence that will contains pairs of the first and the last characters in a words.
Let start from the description of operators [] of class std::basic_string ([string.access]). The decription points out only one requirement pos <= size.If pos exactly less than size() then operators return *(begin() + pos). Otherwise, they return "a reference to an object of type charT with value charT(), where modifying the object leads to undefined behavior" (quatation of the C++ STandard).
Now let consider the decription of member functions front(). There is written that effects of their usage is equivalent to operator [](0).
What does this mean? it means that if an object is empty the functions will return "a reference to an object of type charT with value charT(), where modifying the object leads to undefined behavior".
It seems that all is o'k. However it is not the truth. The description of member functions front() requires that the condition !empty() were satisfied.
So this codestd:;string s;if ( s[0] ) { /*...*? }is valid while this codestd::string s;if ( s.front() ) { /*...*? }is wrong.
The worse situation with member functions back(). They are equivalent to operator[](size() - 1). Take into account the expression in the parentheses.
Now let consider a simple task check whether the first and the last characters ina string are equal.
If the string is empty you can write on the one hand s[0] to denote the first character.
But what about the last character?
Can you write s[ s.size() -1]? No you can not because if the string 's' is empty this expression is equivalent to s[-1].
ISo you need to make the code more compound. You need check whether the string is empty that to know may you directly compare the first characterv with the last.For example you can writeif ( s.empty() || s[0] == s[s.size() - 1 ) { /*...*/ }This is not very readable record. It would be much simpler to writeif ( s.front() == s.back() ) { /*...*/ }This statement is very clear/
I do not see any great sense in this requirement of the C++ Standard. Moreover it looks like a defect. In my opinion the both functions, front() and back(), indeed shall be equivalent to operators [] that is for example member functions back shall be equivalent to "operator[](size() - 1)" if the condition !empty() is true. "Otherwise, return a reference to an object of type charT with value charT(), where modifying the object leads to undefined behavior"
So I suggest remove the requirement !empty() from the descriptions of front() and back(). This requirement only confuses users and prevents to write generic
clear and readable code.
You should take into account the very important difference between "usual" arrays and character arrays. "Usual" arrays may not be empty while character arrays in fact can be empty.
I have read this and have nothing rationale against that front and back would return terminating zero for emplty strings.
Terminating zero is not a string character? And what?! It is no9t an argument.
operator []( 0 ) also returns non-character for an empty string.
I need a safe simple method to use front and back in generic
code.
It is invariant that operator []( 0 ), front() and back() shall return terminating character for an empty string.
It is a very artificial, useless, and only confusing requirement that back and front may be applied only for non empty strings.
If somebody wants to make his life more complicated and writes.size() == 0 ? s[0] : s.front();it is his problem. Why should other suffer from such a stupidy?
class std::string as a wrapper for C sttrings that provides the memory management.
Otherwise it would not exist and you would use std::vector<char>.
It is the reason that operator []( 0 ) returns 0.for an empty string.
But it is very strange and inconsistently that back and front has undefined behavior for an empty string.
I am not speaking even that compilers supply in fact 0 for front for an empty string because thay simply should return something.
The program shall not alter any of the values stored in the character array
One again I do not see any serious arguments. I see only a lack of understanding.
Well let return to the realizations of the same functions in C that were shown already above. I will show them again.char front( const char *s )
{
return s[0];
}
char back( const char *s )
{
size_t n = strlen( s );
return ( ( n == 0 ) ? s[0] : s[n - 1] );
}Are these function written in the spirit of C? Yes, they are. Are these functions safe? Yes, they are except that they do not check that the parameter can be NULL. But neither C string function checks this condition.
So what are you proposing?You are saying: "Let write this functions unsafe!":) And you are writingchar back( const char *s )
{
size_t n = strlen( s );
return ( s[n - 1] );
}And you are adding that somewhere in the C++ Standard we will write that the function has undefined behavior if a string is empty.Moreover this function does not signal that its usage for an empty string is invalid. Neither exception no some return code that will signal its bad usage. So it is written neither in the spirit of C no in the spirit of C++.
And what about users?As you removed the check ( n == 0 ) ? s[0] : s[n - 1] from the function body you are now suggesting users to make this check theirself each time when they will call the function.
Splendid! My applause!:)The only problem that users will ask the reasonable question: "Why do we need such a function if in fact we shall do all ourselves? This function is very dangerous and should not be used":)
In my opinion this function is unsafe, provokes to errors that are difficult to find out,,only confuses users and is useless.
One more I do not see any serious arguments.
You are riting for example that "operator[] is the only thing in std::string that is meaningful for an empty string". Why?!
I do not see any sense in this operator behaviour without corresponding bahaviour of for example front.
If you said 'A' then please said also 'B'. Either operator [](0 ) has a sense for an empty string and in this case front has the same sense or trhe both have no sense and behave the same way as the corresponding functions in any other sequential container.
I do not understand why I need such a stupid compound code? Can you explain me?
As for me it is enough to write correctly function front. As for you you may build one template function over another template function. It is you problem. Why are you tryying to force others to write a bad code?!
As for functions data and c_str then the only requirement in the C++ 11 standard is the followingThe program shall not alter any of the values stored in the character array
So if functions front and back will be correctly described in the Standard it will not change the currrent code base.
This"Code that uses front() or back() on empty strings is wrong. "is a wrong atatement. It is the same as to say for C functions that C string functions that use empty strings that is strings that have no characters except the terminating zero are wrong.
I showed C functions front and back and in my opinion the equivalent C++ functions front and back shall be the same.
I need safe simple functions front and back without bothering that strings can be empty.
As for the debug assertion then it is not very useful.
For example you are reading some file into a vector of std::pair<char, char> where the pair contains the first and the last symbols of a string.. And you are not bothering that a string can be empty. For example the file can contain two sequential new line characters.
In this case you could use std::transform as I showed here somewhere above with simple fiunction[]( const std::string &s ) -> std::pair<char, char> { return {s.front(), s.back() }; }In other words it is natural that this code would be valid for any string
auto lm = []( std::string &s ) -> std::pair<char, char>
{
return { s.front(), s.back() };
};I do not want to write in this simple case something asauto lm = []( std::string &s ) -> std::pair<char, char>
{
if ( s.empty() ) return { 0, 0 };
else return { s.front(), s.back() };
};;
This"Code that uses front() or back() on empty strings is wrong. "is a wrong atatement. It is the same as to say for C functions that C string functions that use empty strings that is strings that have
no characters except the terminating zero are wrong.I showed C functions front and back and in my opinion the equivalent C++ functions front and back shall be the same.
I need safe simple functions front and back without bothering that strings can be empty. If I need to check that strings are not
One more I do not see any serious arguments.
Your C function "front" is so incorrect it's brain-damaged.
On 23/04/13 19:19, Vlad from Moscow wrote:
>
> I simply do not see any serious arguments except unwillingnessto
> confess the wrong approach.
> Well I will try to explain.
> For any indexed sequence in any programming language the notion of
> front() is defined as the notion s[0] (let assume that indexes start
> from 0). If the expression s[0] is valid then the expression front()
> is also valid and vice versa. These two expressions are interchangeable.
> The same way is defined the notion of back(). It is defined as an
> expression with the maximum index n for which s[n] is valid. If there
> is no such an index that greater than 0 then it means that back() is
> equivalent to s[0] provided that the expression s[0] is valid.
The standard promises that s[size()] is valid, and always returns
charT(). By this argument, do you want 'back()' to always return
charT()? This does not seem like it would be useful (and would be a
breaking change).
This shows exactly the problem -- std::string tries to provide something
both like a container, where you can access the on-past-end-value with
s[size()]. However, when the container is empty, front() and back()
still do not make sense.
Chris
I simply do not see any serious arguments except unwillingness to confess the wrong approach.
For any indexed sequence in any programming language the notion of front() is defined as the notion s[0] (let assume that indexes start from 0). If the expression s[0] is valid then the expression front() is also valid and vice versa. These two expressions are interchangeable.
The same way is defined the notion of back(). It is defined as an expression with the maximum index n for which s[n] is valid. If there is no such an index that greater than 0 then it means that back() is equivalent to s[0] provided that the expression s[0] is valid.
Now let consider a regular arrays. They can not be declared as having zero elements. So for an empty array the notions a[0] and front are invalid. Also take into account that for any regular array back() is always corresponds to a[dimension - 1] if dimension is not equal to zero (that may not be).
So similar to the behaviour of regular arrays any empty object of std::vector has undefined expressions v[0] and v.back().
The other situation with character arrays. Again we may not define a character array with the zero dimension. However even if a character array has the dimension that is greater than zero we can say that a character array is empty if its first byte is the zero-terminating byte. So even for so-called empty character arrays expression s[0] is valid.
Class std::string is created that simulate character arrays and string literals. If we are saying that expression s[0] is valid then it means that expressions s.front() and s.back() is valid because they are interchangeable.
Now let return to the simple task of creating of a container of type std::vector<std::pair<char, char>> that is built based on some other container of type std::vector<std:;string> where some strings can be empty.If you are describing some task and the description contains some pre-condition then these pre-conditions should be reflected in the corresponding code.For example you are saying: "I want for a non-empty string to get the first and the last characters."In this case the corresponding code could look the following waystd::pair<char, char> p;if ( !s.empty() ) p = std::make_pair( s.front(), s.back() );Here !s.empty() is the pre-condition that was declared in the description.Now let assume you are saying: "I want to get the first and the last characters for sequence of strings."
There is no any pre-conditions. What to do in this case? Let assume that in this case it is not important whether some strings are empty or not. You have the container std::vector<std:;string> and have to build the corresponding container std::vector<std::pair<char, char>>. So you even do not know what to do.:)
Well, David Rodriguez Ibeas suggested to use the following functionality[]( const std::string &w ) -> std::pair<char,char>
{
return w.empty() ? {0,0} : {w.front(), w.back()};
} );
But why did he decide that {0, 0} shall be returned in case of an empty string?! Why not {-1, -1} or even {'$', '$'}?
So one programmer will use {0,0}, the second programmer will use {-1, -1} the third will use {'$', '$'} and so on. The notions of front() and back() for an empty string is not defined in the Standard.
So it looks like this simple task is insoluble.
If s[0], front() and back() would be equivalent for empty strings (and they shall be equivalent) then there is no any problem. The task is being done using standard algorithm std::transformstd::vector<std::string> v;// filling the vector from some file that can contain empty records.std::vector<std::pair<char, char>> v2;v2.reserve( v.size() );std::transform( v.begin(), v.end(), std::back_inserter( v2 ), [](const std:;string &s ) ->std::pair<char, char> { return { v.front(), v.back() }; } );Why did you decide that I shall skip empty strings? Did I ask you about this? Is there some pre-conditions in my description of the task? No. there is not. I am satisfied with the result.
If I need to skip empty strings I can do that further while processing the new vector using expressionif ( p.first ) { /* some code */ }
This expression is valid because I am sure that I am dealing with a text file and strings can not contain embedded zeroes.
So there are two approachesOn the one hand there are unsave functions that have confusing semantic because they are not equivalent to s[0] and provide unpredictable behaviour in case when a string is empty because it is not clear what to return for empty strings. They are sources of numerous errors. One programmer do not know that these functions have undefined behavior for empty strings. Other programmer knows that but forgot to insert the check of empty strings. The third simply do not know what to return in case of empty strings.On the other hand there are safe functions with clear consistent logic and predictable behavior. You need check whether a string empty? No problem! Use expression s.empty().So either s[0], front() and back() are not defined for an empty container (as for example for std::vector) or if one of them is defined then the others also shall be defined.
On Tuesday, April 23, 2013 10:45:39 PM UTC+4, Chris Jefferson wrote:On 23/04/13 19:19, Vlad from Moscow wrote:
>
> I simply do not see any serious arguments except unwillingnessto
> confess the wrong approach.
> Well I will try to explain.
> For any indexed sequence in any programming language the notion of
> front() is defined as the notion s[0] (let assume that indexes start
> from 0). If the expression s[0] is valid then the expression front()
> is also valid and vice versa. These two expressions are interchangeable.
> The same way is defined the notion of back(). It is defined as an
> expression with the maximum index n for which s[n] is valid. If there
> is no such an index that greater than 0 then it means that back() is
> equivalent to s[0] provided that the expression s[0] is valid.
The standard promises that s[size()] is valid, and always returns
charT(). By this argument, do you want 'back()' to always return
charT()? This does not seem like it would be useful (and would be a
breaking change).Yes I want that if s[0] is valid for an empty object of std::string then s.front() and s.back() shall be also valid. As I think I demonstrated that in this case we will have a predictable behavior that allow to write generic code.
There is such a language as C that considers character arrays as empty if the first character is the terminating zero.:)
If there would not be such conception then C++ would not have container std:;string. It would suggest to use std::vector<char> for character arrays.:)
Yes I want that if s[0] is valid for an empty object of std::string then s.front() and s.back() shall be also valid. As I think I demonstrated that in this case we will have a predictable behavior that allow to write generic code.
On Tue, Apr 23, 2013 at 2:19 PM, Vlad from Moscow <vlad....@mail.ru> wrote:I simply do not see any serious arguments except unwillingness to confess the wrong approach.
"Wrong" is your statement, where everyone else seems to consider yours to be the "wrong" approach.
For any indexed sequence in any programming language the notion of front() is defined as the notion s[0] (let assume that indexes start from 0). If the expression s[0] is valid then the expression front() is also valid and vice versa. These two expressions are interchangeable.
Incorrect, 'front()' is the _first_ element in the sequence, 'back()' is the _last_ element in the sequence. In most containers 'front()' is exactly the same as '[0]' and can be implemented as such. But do not confuse the semantics with the implementation. This is what others have been trying to explain. The std::string is designed as a type that stores a sequence of characters (including possible nulls) and can be empty. That is the *design*.
s[0] is also the _first_ alement in a sequence is not it? :) Well what does mean the _last_ element? Is the valid expression s[n] with the meximum index n is the _last_ element of an indexed sequence?:) What you are saying is not an argument. It is a play on words. You are trying to introduce some notion of the _last_ element that is not expressed throufg a valid expression s[n].
The same way is defined the notion of back(). It is defined as an expression with the maximum index n for which s[n] is valid. If there is no such an index that greater than 0 then it means that back() is equivalent to s[0] provided that the expression s[0] is valid.
Reread your sentence, and try to determine whether it makes sense. "back() is equivalent to s[0] provided that the expression s[0] is valid". This is a very limited definition of what the general notion of 'back()' is considering that in all other situations an empty container does not have a 's[0]'.
The definition of your 'front()' and 'back()' concepts differ from what everyone else considers, and they are not better. In the standard case you have to special case accessing the element at the front and the back (this is really what 'front()' and 'back()' mean) for empty strings because an empty string does not contain any element in the front or the back. It does not contain any element period.
It looks like you do not understand what you are writing. In fact you are writing what I lready said that functions front() and bac() are useless.:) Think about this.
Now let consider a regular arrays. They can not be declared as having zero elements. So for an empty array the notions a[0] and front are invalid. Also take into account that for any regular array back() is always corresponds to a[dimension - 1] if dimension is not equal to zero (that may not be).
Wrong:
int *p = new int[0];
Now try to apply your rationale to this case. There are no elements, accessing p[0] is undefined behavior, as is accessing p[0-1].
So similar to the behaviour of regular arrays any empty object of std::vector has undefined expressions v[0] and v.back().
See code above
The other situation with character arrays. Again we may not define a character array with the zero dimension. However even if a character array has the dimension that is greater than zero we can say that a character array is empty if its first byte is the zero-terminating byte. So even for so-called empty character arrays expression s[0] is valid.
Null terminated strings, which are *not* just character arrays, have this behavior, yes.Class std::string is created that simulate character arrays and string literals. If we are saying that expression s[0] is valid then it means that expressions s.front() and s.back() is valid because they are interchangeable.
The class std::string has been designed to represent a _string_, not a character array, not a null terminated C string. It has been adapted to provide some level of compatibility, but that is not the purpose of the type.
Now let return to the simple task of creating of a container of type std::vector<std::pair<char, char>> that is built based on some other container of type std::vector<std:;string> where some strings can be empty.If you are describing some task and the description contains some pre-condition then these pre-conditions should be reflected in the corresponding code.For example you are saying: "I want for a non-empty string to get the first and the last characters."In this case the corresponding code could look the following waystd::pair<char, char> p;if ( !s.empty() ) p = std::make_pair( s.front(), s.back() );Here !s.empty() is the pre-condition that was declared in the description.Now let assume you are saying: "I want to get the first and the last characters for sequence of strings."The problem here is that the problem definition is _flawed_. Go ask any non C developer what the first character of an empty string is. The reality is a text, null termination is an artifact of the C implementation. Before you wanted to compare 'front()' and 'back()' with different languages. How many programming languages have null termination, in how many programming languages can you do 's[0]' blindly for a string?
There is no any pre-conditions. What to do in this case? Let assume that in this case it is not important whether some strings are empty or not. You have the container std::vector<std:;string> and have to build the corresponding container std::vector<std::pair<char, char>>. So you even do not know what to do.:)
:) This is sooo untrue. There is the implicit precondition that you are not stating here that the strings are null terminated. Just consider giving that same problem to a developer in a different language.
Well, David Rodriguez Ibeas suggested to use the following functionality[]( const std::string &w ) -> std::pair<char,char>
{
return w.empty() ? {0,0} : {w.front(), w.back()};
} );
But why did he decide that {0, 0} shall be returned in case of an empty string?! Why not {-1, -1} or even {'$', '$'}?
I tried to model what you *wanted* and was not explicit in the problem, exactly because you did not offer an alternative. I believe I even went further and said that your approach would solve this particular definition of the problem but not any other definition in which a different pair was to be generated.
So one programmer will use {0,0}, the second programmer will use {-1, -1} the third will use {'$', '$'} and so on. The notions of front() and back() for an empty string is not defined in the Standard.
Exactly right: each programmer, given a well formed problem will choose to generate code that complies with the requirements. In some cases you might want to use '\0' while in others a different form of separator. Unless you understand that your problem description is incomplete in assuming that strings are null terminated you won't understand this part. Give the same task to someone programming in a different language. Would you state the same problem in a different way in Java?
So it looks like this simple task is insoluble.
Well, I believe I provided a rather simple solution to it, which you might not like, but does it not yield the exact same set of std::pair<char,char> that you wanted (and did not formally state)?
If s[0], front() and back() would be equivalent for empty strings (and they shall be equivalent) then there is no any problem. The task is being done using standard algorithm std::transformstd::vector<std::string> v;// filling the vector from some file that can contain empty records.std::vector<std::pair<char, char>> v2;v2.reserve( v.size() );std::transform( v.begin(), v.end(), std::back_inserter( v2 ), [](const std:;string &s ) ->std::pair<char, char> { return { v.front(), v.back() }; } );Why did you decide that I shall skip empty strings? Did I ask you about this? Is there some pre-conditions in my description of the task? No. there is not. I am satisfied with the result.
There *are* preconditions, the precondition that strings are null terminated.
If I need to skip empty strings I can do that further while processing the new vector using expressionif ( p.first ) { /* some code */ }
:) Bug report, you fail to apply 'some code' to my perfectly valid string:
std::string s(10,' '); s[0] = 0;
Why did you not apply 'some code' there! Why did you assume that strings would not contain a null? Did I tell you to ignore this case?
This expression is valid because I am sure that I am dealing with a text file and strings can not contain embedded zeroes.
Good, so there are even more preconditions that you failed to mention in your problem definition! So much for 'Is there some pre-conditions in my description of the task? No'
So there are two approachesOn the one hand there are unsave functions that have confusing semantic because they are not equivalent to s[0] and provide unpredictable behaviour in case when a string is empty because it is not clear what to return for empty strings. They are sources of numerous errors. One programmer do not know that these functions have undefined behavior for empty strings. Other programmer knows that but forgot to insert the check of empty strings. The third simply do not know what to return in case of empty strings.On the other hand there are safe functions with clear consistent logic and predictable behavior. You need check whether a string empty? No problem! Use expression s.empty().So either s[0], front() and back() are not defined for an empty container (as for example for std::vector) or if one of them is defined then the others also shall be defined.
I see two approaches, you use std::string that is designed to support nulls internally and for which 'front()' and 'back()' (together with '*begin()' and '*(end()-1)') are undefined behavior for an empty string, or you design a different string class and use it.
One of the things you keep saying is that your approach is 'safer', well, it is not necessarily safer. Consider that in my domain empty strings should never be passed to a function, and that with that in mind the function accesses `front()` directly (more probably '*begin()'). My standard library implementation can assert and let me know during debugging that there is an issue in the program that I can fix before going to production. Narrow contracts are not unsafer than wide contracts.
Well in fact function front() can not be used along in generic code. It always shall be accpmpanied with the condition !s.empty() shall not it? So if you want to have a safe code you need to substitute it everywhere in the code for s[0]. In this case you will have a predictable behavior: if a string not empty you will get an actual character otherwise you will get 0. It means only the following that member function front is totally useless.
If you are speaking about semantic then it is very simple: if expression s[0] is valid for an empty sequence then expressions s.front() and s,back() are also valid. Otherwise you are trying only to confuse users.
Well in fact function front() can not be used along in generic code. It always shall be accpmpanied with the condition !s.empty() shall not it? So if you want to have a safe code you need to substitute it everywhere in the code for s[0]. In this case you will have a predictable behavior: if a string not empty you will get an actual character otherwise you will get 0. It means only the following that member function front is totally useless.Well you can substitute front() for s[0]. But what about back()?:) I do not see any other way that to write
You have here a pointer. You have no direct access to the array You may noy place an element in this arrray. So this example does not contradict to what I said.
The class std::string has been designed to represent a _string_, not a character array, not a null terminated C string. It has been adapted to provide some level of compatibility, but that is not the purpose of the type.
It is your personal opinion. I think that the main purpose was to adapt safe using of character arrays in C++
It is not important how many languages use this idiom. It is important that s[0] is valid expression for empty string in C and C++.
I never said that the strings are null-terminated. It is your fantasy. I relied on the C++Standard that s[0] returns '\0' for empty strings.And I suggested that the bcode will be safe that front() and back() also will return '\0' for empty strings. It is main idea of the proposal that s[0], front and back would be equivalent for empty strings. It is not even important what value they will return. It is much more important that the bahavior were defined and predictable. At present the behaviour is undefined, there is no some exception or other means of signalling an error and unpredictable.
I tried to model what you *wanted* and was not explicit in the problem, exactly because you did not offer an alternative. I believe I even went further and said that your approach would solve this particular definition of the problem but not any other definition in which a different pair was to be generated.
Do not model. Say what are you suggesting? What is your alternative> And are you sure that other programmer will use your alternative? How many are such alternatives?:)
One more I want that by default the bahavior were predictable. Do not tell me stories about many cases.
Well, I believe I provided a rather simple solution to it, which you might not like, but does it not yield the exact same set of std::pair<char,char> that you wanted (and did not formally state)?No, it is not the same pair as you think. My pair is generated by the values of back and front. These values are predefined according to my proposal. Your values are arbitrary and nobody can rely on them because in some other part of the project other programmer can use other values..
There *are* preconditions, the precondition that strings are null terminated.It is not a precondition and strings are not null terminated as you think. I am suggesting that s[0], front() and back behave the same way and would return the same value for empty strings.
I already explained that this string is not perfectly valid because you are dealing with a text file.
Yes I ignore this case. You have invalid data. So your should check the input. It is not the problem of this task. The error occured somewhere else. For example input data wes corrupted and this situation was not processed.
One of the things you keep saying is that your approach is 'safer', well, it is not necessarily safer. Consider that in my domain empty strings should never be passed to a function, and that with that in mind the function accesses `front()` directly (more probably '*begin()'). My standard library implementation can assert and let me know during debugging that there is an issue in the program that I can fix before going to production. Narrow contracts are not unsafer than wide contracts.
I do not see any problem. You library will be unchanged because it will always assert if an empty string will be passed.:)
And I assume that *s.begin() and *s.end() should also be valid? You can define the contract of 'front' in terms of 'begin' in the same way that it is expressed in terms of 'operator[]'. But now the change means that 'end()' is suddendly a deference-able pointer, do you really want that? Or it is perfectly fine to have the inconsistency between 'front()' and '*begin()' but not between 'operator[]' and 'front()'. What about 'at()'?
I said clearly enough that all I want is that s[0], front() and back() have the same behavior for empty strings. Nothing more.
No. That's not at all what it means. Just because front() doesn't perform the same checking as operator[] doesn't
make front() useless. It makes front() quite the opposite, it makes it very useful.
General case is the case when you can not guarantee that a sequence will not contain empty strings. So because front and back can not be used in the feneral case they are totally useless. Moreover they can not be used in generic code. Again due to the
As I said in the very beginning I do not see any serious counter-evidences and you confirmed that indeed there are no counter-evidences.I demonstrated that functions front() and back() are totally useless, unsafe and their usage should be avoided..
2013/4/23 Vlad from Moscow <vlad....@mail.ru>:
>
> I simply do not see any serious arguments except unwillingnessto confess the
> wrong approach.
>
> Well I will try to explain.
>
> For any indexed sequence in any programming language the notion of front()
> is defined as the notion s[0] (let assume that indexes start from 0). If the
> expression s[0] is valid then the expression front() is also valid and vice
> versa. These two expressions are interchangeable.
Note that s[0] for an empty basic_string still is restricted compared
to other index accesses < size(), since modifying the character is
undefined behaviour. This is really an extreme borderline case and as
others have already noticed, the at() functions would throw an
exception here, so this is clearly not comparable with other indexed
positions. I would argue here that front() are well-defined, if at(0)
does not throw an exception. I see no good reason to extend this
special access any further, this would give the wrong signal to the
user that read or potentially write access might be freely allowed.
Given the potential danger of misusing this facility I see not a
considerable win of the suggested extension for the user. Furthermore,
in any generic context, this special rule would not be applicable to
any other container-like type. This functionality might be useful to
you and probably to several other people. Nonetheless the Standard
Library does not standardize *everything* that *can* be useful.
> The same way is defined the notion of back(). It is defined as an
> expression with the maximum index n for which s[n] is valid. If there is no
> such an index that greater than 0 then it means that back() is equivalent to
> s[0] provided that the expression s[0] is valid.
See above.
> Now let consider a regulat arrays. They can not be declared as having zero
> elements. So for an empty array the notions a[0] and front are invalid. Also
> take into account that for any regular array back() is always corresponds to
> a[dimension - 1] if dimension is not equal to zero (that may not be).
I sympathize with your view, but basic_string is - more or less - a
container with a special end character that is read-only. The actual
elements are the characters before this end character. You can
consider this end-character as a special manifestation of a
past-the-end iterator value. The front()/back()/at functions are
supposed to model the access to the *elements* and the special
past-the-character does not belong to these elements.
> So similar to the behaviour of regular arrays any empty object of
> std::vector has undefined expressions v[0] and v.back().
Yes.
> The other situation with character arrays. Again we may not define a
> character array with the zero dimension. However even if a character array
> has the dimension that is greater than zero we can say that a character
> array is empty if its first byte is the zero-terminating byte. So even for
> so-called empty character arrays expression s[0] is valid.
But the validity of access is not the same as for other characters,
because it is read-only. This is very similar to the past-the-end
iterator where no guarantee exists that you can dereference it.
> Class std::string is created that simulate character arrays and string
> literals. If we are saying that expression s[0] is valid then it means that
> expressions s.front() and s.back() is valid because they are
> interchangeable.
Keep in mind that we also have at(0) and this is a counter example to
your model.
> Now let assume you are saying: "I want to get the first and the last
> characters for sequence of strings."
>
> There is no any pre-conditions.
There is one, that you shall not modify the value of this past-the-end
character.
> This expression is valid because I am sure that I am dealing with a text
> file and strings can not contain embedded zeroes.
In your example they may not, but basic_string is designed to support
embedded zero characters.
> So either s[0], front() and back() are not defined for an empty container
> (as for example for std::vector) or if one of them is defined then the
> others also shall be defined.
I agree that consistency is useful, but sometimes we have to make
compromises when we have to consider the historic context or when we
take a broader view on this comparing with related concepts.
front()/back() are designed to specify the accessible limits of the
elements of a sequence and therefore the current result for string is
consistent (In addition to the outcome of at), because the final
null-terminator is no element. If we would attempt to enforce the
ideal at all costs, I would vote for making the access s[0] for an
empty string s invalid. But for historic reasons this has been decided
for to be valid. Changing this now would break the contract of large
amount of code and at least to me the win in consistency is not worth
this risk. If you find the inconsistency so repellent, I can only
suggest to (a) attempt to view on basic_string as *if* s[0] is invalid
for an empty string or (b) to provide your own helper functions that
fix the wrong view of basic_string for you.
- Daniel
As I said in the very beginning I do not see any serious counter-evidences and you confirmed that indeed there are no counter-evidences.
I demonstrated that functions front() and back() are totally useless, unsafe and their usage should be avoided..
I see teh only problem that all you are unable to understand that my example is not a "special scenario". It is a general case when a container can contain any strings including empty. Even if you initially have a container without empty strings however due to some processing empty strings can appear in it. So the usage of front() and back() are unsafe and useless. And I demonstrated this.
On Wed, Apr 24, 2013 at 4:49 AM, Vlad from Moscow <vlad....@mail.ru> wrote:I see teh only problem that all you are unable to understand that my example is not a "special scenario". It is a general case when a container can contain any strings including empty. Even if you initially have a container without empty strings however due to some processing empty strings can appear in it. So the usage of front() and back() are unsafe and useless. And I demonstrated this.
It *is* a special scenario for different things. The first is how often the problem of extracting the first and last character of the string happen in real life? (infrequent implies somehow special).
In how many cases for an empty string you would want to generate a value rather than say skip it?
(this is just one of the options, so it is only a subset of the previous). How many times if you want to produce a value that value needs to be 'std::pair<char,char>(0,0)'? (why not 'std::pair<char,char>('^','$')'?) You are not aiming to solve a *general* problem, but a very particular one, and you want a change in the library that will make your *particular* use case simpler in just a couple of characters.
If the pair to be stored was not (0,0) then you would have to fall back to the code I produced. If empty strings where not to be processed then you would have a similar check (if (!s.empty())...), if...
--
---
You received this message because you are subscribed to the Google Groups "ISO C++ Standard - Discussion" group.
To unsubscribe from this group and stop receiving emails from it, send an email to std-discussio...@isocpp.org.
To post to this group, send email to std-dis...@isocpp.org.
Visit this group at http://groups.google.com/a/isocpp.org/group/std-discussion/?hl=en.
You should always avoid to write functions with undefined behavior. It is simply a bad design of a function and very bad style of