-11
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?

Pixelchemist
  • 24,090
  • 7
  • 47
  • 71
larry
  • 1

1 Answers1

10

Short

  1. The code doesn't work because it is defined in the scope of the if branch.
  2. 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; 
}
Pixelchemist
  • 24,090
  • 7
  • 47
  • 71