6

I'm new to C++11, and quite frankly, I haven't used C++ in well over a year, so I'm a bit rusty to begin with. I'm doing some exercises from my old college text, and I've run into a problem while trying to iterate over a char pointer string (Ex: char * c = "a string";). I haven't been able to find anything helpful on Google. I'm so used to Java where the foreach loop can just iterate over any collection. I know how pointers work, but my long vacation from C++ has left me clueless about the syntax of actually using them. Can someone tell me why the following code (specifically, the convert() function) causes an error in compilation, in which it says "begin" and "end" were not declared in this scope?

Ex12_01_RomanType.h

#ifndef EX12_01_ROMANTYPE_H
#define EX12_01_ROMANTYPE_H

class RomanType {
  public:
    RomanType();
    RomanType(char * n);
    virtual ~RomanType();

    char * toString();
    int getValue();
  private:
    char * numeral;
    int decimal;

    void convert();
};

#endif // EX12_01_ROMANTYPE_H

Ex12_01_RomanType.cpp

#include "Ex12_01_RomanType.h"

RomanType::RomanType() {
  // Default Constructor
  numeral = "I";
  decimal = 0;
  convert();
}

RomanType::RomanType(char * n) {
  // Specific Constructor
  numeral = n;
  decimal = 0;
  convert();
}

RomanType::~RomanType() {
  delete numeral;
}

char * RomanType::toString() {
  return numeral;
}

int RomanType::getValue() {
  return decimal;
}

void RomanType::convert() {
  /* Iterates over each character in the numeral string and adds that
     character's value to the decimal value. This method should only
     be called once during the constructor. */
  for(char c : numeral) {
    if(c == 'M') decimal += 1000;
    else if(c == 'D') decimal += 500;
    else if(c == 'C') decimal += 100;
    else if(c == 'L') decimal += 50;
    else if(c == 'X') decimal += 10;
    else if(c == 'V') decimal += 5;
    else if(c == 'I') decimal += 1;
    else decimal += 0;
  }
}

Sorry if this question seems basic to some. Java has spoiled me.

Darin Beaudreau
  • 375
  • 7
  • 30
  • 1
    Range based for-loop works on container or array types. Here `numeral` is of type `char*`. [Demo example](http://ideone.com/EvgGXl) – Mahesh Aug 02 '13 at 15:22
  • If `numeral` was properly implemented as a `std::string`, this would work. Modern C++ style avoids manual memory management and use of raw pointers as much as possible. – Casey Aug 02 '13 at 15:23
  • char * does not have a defined begin or end iterator. See this link for a good walk through http://www.cprogramming.com/c++11/c++11-ranged-for-loop.html – Paul Renton Aug 02 '13 at 15:23
  • You can't iterate over a `char*` directly with the range-based for loop, as it isn't clear to the compiler how long the string is (it's not a container). You can write an adapter, though, that allows iterating over null-terminated strings. – dyp Aug 02 '13 at 15:24
  • 1
    This class has undefined behavior when default constructed `numeral` is pointed at a string literal in the default constructor, which will be deleted in the destructor. – Casey Aug 02 '13 at 15:26
  • You can use [`boost::iterator_range`](http://www.boost.org/libs/range/doc/html/range/reference/utilities/iterator_range.html) for doing this `for( char c : boost::iterator_range( numeral, numeral + strlen(numeral) ) ) { ... }`. Also pay attention to what Casey says in the comment above. – Praetorian Aug 02 '13 at 15:35

4 Answers4

9

Range-based-for works on arrays, classes with member begin and end, or when there is an appropriate non-member begin and end.

You cant find the "end" of a c-style string (pointer to NUL-terminated char array) without walking all the way through it.

The simplest fix is just to use std::string, which will work with range-based-for.

(Edit: Now that I think of it, be careful even if you use a char array directly:

const char myString[] = "Hello";
for ( auto c : myString ) //...

because you will process all 6 members of the array, including the NUL-terminator. Although you can't do that here.)

BoBTFish
  • 19,167
  • 3
  • 49
  • 76
  • I guess I was just trying to understand how char pointers were used as strings back in the days of C. Oh well... – Darin Beaudreau Aug 02 '13 at 15:25
  • Using std::string is of course the best solution, but it's also worth mentioning the problems of the `numeral = "I"` assignment. It doesn't copy the contents of the literal, it just copies its address. Also, it's assigning the address of the literal to a non-const `char*`. – Nikos C. Aug 02 '13 at 15:25
  • @DarinBeaudreau Nowadays, if I find myself reaching for plain C pointers, I try to take a deep breath and reconsider. – Joachim Isaksson Aug 02 '13 at 15:31
  • @DarinBeaudreau: C-style string pointers are used in conjunction with library functions, hand-written loops, and careful management of the underlying array. In C++, classes like `std::string` present a friendly interface and hide the fiddly details. – Mike Seymour Aug 02 '13 at 15:32
  • Part of the reason I was using this instead of a string is because I wanted to use instead of because I don't particularly like the "cout" style... I like using functions to do that kind of thing. – Darin Beaudreau Aug 02 '13 at 15:36
  • `cout << something` *is* a function call. But in many cases you can use [`std::string::c_str`](http://en.cppreference.com/w/cpp/string/basic_string/c_str). But range-based-`for` is a modern c++ feature, designed around modern c++ style. – BoBTFish Aug 02 '13 at 15:41
1

Char's do not have a begin or an end, so the range-based for loop will not work on a char type, use the newer std::string instead.

0

I think the easiest way to solve your problem is to create a string using its range constructor std::string(char* begin, char* end) and call a for each loop on it.

tomi.lee.jones
  • 1,563
  • 1
  • 15
  • 22
0

You already have the answers. If you want to do this more in c++11 way try to replace all char * by std::string. Today's c++ is if you can do your stuff without new/delete try that first.

If you're in Roman numbers exercises this link is step by step explanation of the decimal to roman with nice c++ http://jonjagger.blogspot.fr/2013/07/larry-and-jen-do-roman-numerals-in-c.html

ColdCat
  • 1,192
  • 17
  • 29