Strange object instantiation - c++

In Bjarne Stroustrup's book:Tour of C++, I have found following snippet:
void test(){
string input = "aa as; asd ++easdf asdfg";
regex pat {R"(\s+(\w+))"};
for (sregex_iterator p(input.begin(),input.end(),pat); p!=sregex_iterator{}; ++p)
cout << (*p)[1] << '\n';
}
I have a problem understanding what is sregex_iterator{} doing. What kind of statement is it? Constructor? Initializer List?

It is creating a (temporary) object of type std::sregex_iterator by calling its default constructor (using uniform syntax). Since C++14, the standard requires that all types that model the ForwardIterator concept be DefaultConstructible and that a default-constructed iterator represents a past-the-end iterator.
From cppreference:
Singular iterators (since C++14)
A value-initialized ForwardIterator behaves like the past-the-end iterator of some unspecified empty container: it compares equal to all value-initialized ForwardIterators of the same type.
So the complete p!=sregex_iterator{}; statement checks whether the iterator p is not yet exhausted.

It is calling the default constructor using Uniform Initialization introduced in C++11.

Related

C++ using default-initialized iterators to create an empty std::string

Is the following code valid and well-defined?
auto start = std::string::const_iterator();
auto end = std::string::const_iterator();
auto output = std::string(start, end);
(The expected output being an empty string.)
According to cppreference.com, a random access iterator, of which a string iterator is one, meets all requirements of a bidirectional iterator.
Furthermore, a bidirectional iterator meets all requirements of a forward iterator.
Finally, since C++14, a forward iterator can be value-initialized, and will compare equal to all other value-initialized forward iterators of the same type:
A value-initialized LegacyForwardIterator behaves like the
past-the-end iterator of some unspecified empty container: it compares
equal to all value-initialized LegacyForwardIterators of the same
type.
Based on that, I believe this is well-defined at least as of C++14.

Is comparison of const_iterator with iterator well-defined?

Consider the following code:
#include <vector>
#include <iostream>
int main()
{
std::vector<int> vec{1,2,3,5};
for(auto it=vec.cbegin();it!=vec.cend();++it)
{
std::cout << *it;
// A typo: end instead of cend
if(next(it)!=vec.end()) std::cout << ",";
}
std::cout << "\n";
}
Here I've introduced a typo: in the comparison I called vec.end() instead of vec.cend(). This appears to work as intended with gcc 5.2. But is it actually well-defined according to the Standard? Can iterator and const_iterator be safely compared?
Surprisingly, C++98 and C++11 didn't say that you can compare a iterator with a const_iterator. This leads to LWG issue 179 and LWG issue 2263. Now in C++14, this is explicitly permitted by § 23.2.1[container.requirements.general]p7
In the expressions
i == j
i != j
i < j
i <= j
i >= j
i > j
i - j
where i and j denote objects of a container's iterator type, either or
both may be replaced by an object of the container's const_iterator
type referring to the same element with no change in semantics.
See §23.2.1, Table 96:
X::iterator
[...]
any iterator category that meets the forward iterator requirements.
convertible to X::const_iterator
So, yes, it is well-defined.
Table 96 in the C++11 Standard, in section 23.2.1, defines the operational semantics of a.cend() for any container type X (including std::vector) as follows:
const_cast<X const &>(a).end()
So the answer is yes because by this definition cend() refers to the same element/position in the container as end(), and X::iterator must be convertible to X::const_iterator (a requirement also specified in the same table(&ast;)).
(The answer is also yes for begin() vs. cbegin() for the same reasons, as defined in the same table.)
(&ast;) It has been pointed out in comments to other answers that convertibility does not necessarily imply that the comparison operation i1==i2 will always work, e.g. if operator==() is a member function of the iterator type, the implicit conversion will only be accepted for the righthand-side argument, not the lefthand-side one. 24.2.5/6 states (about forward-iterators a and b):
If a and b are both dereferenceable, then a == b if and only if *a and *b are bound to the same object
Even though the iterators end() and cend() are not dereferenceable, the statement above implies that operator==() must be defined in such a way that the comparison is possible even if a is a const-iterator and b is not, and vice versa, because 24.2.5 is about forward-iterators in general, including both const- and non-const-versions -- this is clear e.g. from 24.2.5/1. This is why I am convinced that the wording from Table 96, which refers to convertibility, also implies comparability. But as described in cpplearner#'s later answer, this has been made explicitly clear only in C++14.

What is the purpose of * in C++?

int main()
{
string s("some string");
if (s.begin() != s.end())
auto it = s.begin();
*it = toupper (*it) ; // Error ; the identifier "it" is undefined
}
Why is *it undefined? And do why we need to use dereference in the iterator dimension?
Short
The code doesn't work because it is defined in the scope of the if branch.
Dereferencing is required to access the actual "content", the iterator is referencing.
Explanation
1. Why is it undefined after the selection statement?
The standard says (emphasis mine) in § 6.4 / 1:
The substatement in a selection-statement (each substatement, in the else form of the if statement) implicitly defines a block scope (3.3). If the substatement in a selection-statement is a single statement and not a compound-statement, it is as if it was rewritten to be a compound-statement containing the original substatement.
The example given in the standard matches the case at hand.
Example:
if (x)
int i;
can be equivalently rewritten as
if (x)
{
int i;
}
Now we need to know how the (implicit) block scope affects the "visibility" of the variable by looking at the referenced § 3.3 (again emphasis mine):
A name declared in a block (6.3) is local to that block; it has block scope. Its potential scope begins at its point of declaration (3.3.2) and ends at the end of its block. A variable declared at block scope is a local variable.
=>There you go: Your variable it is in a block and the scope of it ends at the end of the block. Thus it is undefined after that block.
2. Why is an iterator to be dereferenced to access the content?
The conecept of iterators is built to abstract pointers as described in chapter 24 for the Iterator library in the standard. Thus, an iterator references something like a pointer also references a value.
Dereferencing is the C++ way of accessing the actual referenced value.
Dereferencing a pointer means "give me the value stored at the memory address of the pointer" whereas dereferencing an iterator means "give me the value stored at the point, the iterator logic is refering to".
(Note: A pointer is just a special kind of iterator.)
The standard requires every iterator type to define the dereferencing operation.
§ 24.2.1 / 1
All input iterators i support the expression *i, resulting in a value of some object type T, called the value type of the iterator. All output iterators support the expression *i = o where o is a value of some type that is in the set of types that are writable to the particular iterator type of i.
Every iterator type is either input or output.
§ 24.2.1 / 2: Types of iterators
This International Standard defines five categories of iterators, according to the operations defined on them: input iterators, output iterators, forward iterators, bidirectional iterators and random access iterators[.]
§ 24.2.1 / 3: Correlation of iterators types
Forward iterators satisfy all the requirements of input iterators and can be used whenever an input iterator is specified; Bidirectional iterators also satisfy all the requirements of forward iterators and can be used whenever a forward iterator is specified; Random access iterators also satisfy all the requirements of bidirectional iterators and can be used whenever a bidirectional iterator is specified.
=> Iterators provide indirection (in a more generalized fashion than pointers do) and need to be dereferenced to follow the indirection, accessing their values.
3. Working example
#include <string>
#include <cctype>
int main()
{
std::string s("some string");
if (s.begin() != s.end())
{
auto it = s.begin();
*it = std::toupper(*it);
}
// it not defined here
return 0;
}

Is it well-defined to compare with a value-initialized iterator?

Does the following program invoke undefined behavior?
#include <iostream>
#include <iterator>
int main(int argc, char* argv[])
{
for (auto it = std::istream_iterator<std::string>(std::cin);
it != std::istream_iterator<std::string>();
++it)
{
std::cout << *it << " ";
}
return 0;
}
This 4 year old question says that they can't be compared:
Iterators can also have singular values that are not associated with
any container. [Example: After the declaration of an uninitialized
pointer x (as with int* x;), x must always be assumed to have a
singular value of a pointer. ] Results of most expressions are
undefined for singular values; the only excep- tion is an assignment
of a non-singular value to an iterator that holds a singular value.
But another answer for says for the C++14 standard:
However, value-initialized iterators may be compared and shall compare
equal to other value-initialized iterators of the same type.
You are conflating two different issues.
istream_iterator is an input iterator, not a forward iterator, so the C++14 change you cited doesn't apply to it at all. You are allowed to compare istream_iterators in that manner because they are explicitly specified to allow such comparisons. The standard says that (§24.6.1 [istream.iterator])
The constructor with no arguments istream_iterator() always
constructs an end-of-stream input iterator object, which is the only
legitimate iterator to be used for the end condition. [...]
Two end-of-stream iterators are always equal. An end-of-stream
iterator is not equal to a non-end-of-stream iterator. Two
non-end-of-stream iterators are equal when they are constructed from
the same stream.
For forward iterators (which also includes bidirectional and random access ones) in general, value-initialized iterators are made comparable to each other in C++14. If your standard library implements it, then you can compare two value-initialized iterators. This allows you to create an empty range without an underlying container. However, you are still not allowed to compare a non-singular iterator to a value-initialized iterator. The following code has undefined behavior even in C++14:
std::list<int> l;
if(l.begin() == std::list<int>::iterator())
foo();
else
bar();

Comparing default-constructed iterators with operator==

Does the C++ Standard say I should be able to compare two default-constructed STL iterators for equality? Are default-constructed iterators equality-comparable?
I want the following, using std::list for example:
void foo(const std::list<int>::iterator iter) {
if (iter == std::list<int>::iterator()) {
// Something
}
}
std::list<int>::iterator i;
foo(i);
What I want here is something like a NULL value for iterators, but I'm not sure if it's legal. In the STL implementation included with Visual Studio 2008, they include assertions in std::list's operator==() that preclude this usage. (They check that each iterator is "owned" by the same container and default-constructed iterators have no container.) This would hint that it's not legal, or perhaps that they're being over-zealous.
OK, I'll take a stab. The C++ Standard, Section 24.1/5:
Iterators can also have singular
values that are not associated with
any container. [Example: After the
declaration of an uninitialized
pointer x (as with int* x;), x must
always be assumed to have a singular
value of a pointer. ] Results of most
expressions are undefined for singular
values; the only excep- tion is an
assignment of a non-singular value to
an iterator that holds a singular
value.
So, no, they can't be compared.
This is going to change in C++14. [forward.iterators] 24.2.5p2 of N3936 says
However, value-initialized iterators may be compared and shall compare
equal to other value-initialized iterators of the same type.
I believe you should pass a range to the function.
void fun(std::list<int>::iterator beg, std::list<int>::iterator end)
{
while(beg != end)
{
// do what you want here.
beg++;
}
}
Specification says that the postcondition of default constructor is that iterator is singular. The comparison for equality are undefined, so it may be different in some implementation.