3

Possible Duplicate:
Where and why do I have to put the “template” and “typename” keywords?

I've (had to :) ) became a C++ developer a few weeks ago (I had some experiences before but not too much, I was more in Java), trying to learn everything which counts and to develop as efficient as I can. So excuse if my question is totally dumb. I have a problem with a simple example template class:

template<typename T>
class SameCounter {
private:
    map<T,int> counted;
public:
    SameCounter(list<T> setup) {
        for(list<T>::iterator it = setup.begin(); it != setup.end(); it++) {
            counted[*it]++;
        }
    }
    map<T,int>::const_iterator& begin() { // line 25
        return counted.begin();
    }
    map<T,int>::const_iterator& end() {
        return counted.end();
    }
};

...
// using the class
Reader rdr;
rdr.Read();
SameCounter<char> sc(rdr.GetData());

I get some error when I'm compiling it:

Error   3   error C4430: missing type specifier - int assumed. Note: C++ does not support default-int   d:\learn_cpp\examples\gyakorlas_1.cpp   25
Error   2   error C2143: syntax error : missing ';' before '&'  d:\learn_cpp\examples\gyakorlas_vizsga\gyakorlas_1.cpp  25

(both of them twice)

I don't have a clue for it, something is wrong maybe with the templating I assume, because if I create the SameCounter as a normal class it is totally ok. Thank you for the help.

Community
  • 1
  • 1
newhouse
  • 1,152
  • 1
  • 10
  • 27
  • You'll need a `typename` before that `lsit::iterator` and likewise for the map's const iterator. – chris Jan 10 '13 at 09:08
  • A bit difficult to answer without knowing what line 25 is (where the error occurs). But in general what these two errors are saying is A, you are trying to define a variable without a type definition and B you forgot to parse something OR that you are attempting to define a function as a reference (you return &val the function if you want to assign it as a pointer use the *) –  Jan 10 '13 at 09:09
  • "strange behavior" is probably the worst title you could choose. Only a few questions are really dumb, this doesn't look like it is, but it's not a well written one. – stefan Jan 10 '13 at 09:15
  • Yeah, sorry for it. I forgot a tiny "problem" occurs with templates when I want to refer on inner types. As answered below. Thanks for all. – newhouse Jan 10 '13 at 09:17
  • 4
    For future reference, when writing the code excerpt, it's a good idea to mark the lines mentioned in the error message (e.g. with a comment like `//line 25 here`. – Angew is no longer proud of SO Jan 10 '13 at 09:19
  • Unrelated: You probably don't want to take the list by value, and you probably don't want to use a `std::list` either (it's a linked list, not what they call "list" in Java; you probably want `std::vector`). – R. Martinho Fernandes Jan 10 '13 at 09:32
  • of course I wanted to use a linkedlist here, but yeah, i did not have to pass it by value to be more efficient – newhouse Jan 10 '13 at 10:06

2 Answers2

9

This should help you:

typename map<T,int>::const_iterator& begin() {
    return counted.begin();
}
typename map<T,int>::const_iterator& end() {
    return counted.end();
}

C++ templates are tricky. T is a template parameter, and map<T, int>::const_iterator could possibly mean different things (type names, but also - say - static members...) depending on what T you pass.

That's why in templates sometimes you need to make your intention clear and indicate that you actually mean "const_iterator is a type and I want a reference to it". The keyword 'typename' allows for that.

See: http://pages.cs.wisc.edu/~driscoll/typename.html


To make your code simpler and avoid reduce the need for typename, you could start with:

private:
    typedef std::map<T, int> MapType;
    MapType counted;

and then just go with

typename MapType::const_iterator &begin() {

Unfortunately this typename still needs to be here, you'd need further typedef typename for each dependent type to remove it from further declarations (see @rhalbersma's answer).


Following @rhalbersma's comment, let me also emphasise that you should return these iterators by-value. Returning references to temporaries causes undefined behaviour because the object gets out of scope and you end up with a "dangling reference".

So make it:

typename MapType::const_iterator begin() {
Kos
  • 70,399
  • 25
  • 169
  • 233
  • Geeez, thanks, I am totally dumb. You were right, I forgot that for templates the :: operator means generally that I refer on a static member and not a typename. Of course. Thank you again. – newhouse Jan 10 '13 at 09:15
  • I've [compiled the code](http://ideone.com/0SDnvN) and you're going to need another `typename` in `list::iterator` in the ctor. Go with the typedef pattern, it simplifies things a lot (and it's commonly used in the standard library - each standard template has plenty of typedefs for its "dependent" types.) – Kos Jan 10 '13 at 09:17
  • BTW newer compilers tend to bless you with errors like `error: need 'typename' before 'std::map::const_iterator' because 'std::map' is a dependent scope` (GCC). – Kos Jan 10 '13 at 09:22
  • 3
    @n3whous3: I'd hardly call you dumb for not being aware of this obscure C++ idiosyncrasy. Even though it's my favorite language, I recognize a wart when I see one. – Benjamin Lindley Jan 10 '13 at 09:25
  • @Kos Why are you returning the iterators by reference instead of by value? – TemplateRex Jan 10 '13 at 09:27
  • @rhalbersma Sorry, I paid attention to the OP's problem and didn't review the code as a whole. (EDIT: mentioned this issue) – Kos Jan 10 '13 at 09:28
  • Yeah, it gives totally irrelevant error messages sometimes, but I like it too. Even more, the most flexible language which I worked with. @Kos: yeah, but strangely, that part of the code, when I iterate with that iterator, the VS compiler did not throw an error. Nonetheless, I wrote typename there also to be accurate. – newhouse Jan 10 '13 at 09:28
  • @rhalbersma I was the one who declared the reference return, that was not really ok of course :) At least this code had some problems afterwards. – newhouse Jan 10 '13 at 09:29
  • @Kos While you're at it: also mark `begin() const` since it is returning a const_iterator ;-) – TemplateRex Jan 10 '13 at 09:33
  • Yeah, I was not aware of strict my code as much as possible, thanks – newhouse Jan 10 '13 at 09:37
2

I've annoted your class below. Several points are worth mentioning:

  template<typename T>
  class SameCounter 
  {
  private:
     typedef map<T,int> MapType; // typedef here to keep specific container in a single place
     typedef typename MapType::const_iterator const_iterator; // to avoid retyping "typename"
     typedef typename MapType::iterator iterator; // to avoid retyping typename
     MapType counted;
  public:
     SameCounter(list<T> setup) {
        // auto here to avoid complicated expression
         for(auto it = setup.begin(); it != setup.end(); it++) {
             counted[*it]++;
         }
     }

    // by value instead of by reference, mark as const member
    const_iterator begin() const {
        return counted.begin();
    }

    // by value instead of by reference, mark as const member
    const_iterator end() const {
        return counted.end();
    }

    // probably best to also forward cbegin()/cend() and non-const begin() / end()
 };
  • inner typedefs come in handy if you ever want to change from map to another container (unorderd_map e.g.) and they avoid repeated typing of typename for nested typedefs.
  • auto (C++11 keyword) can limit typing complicated iterator types
  • return by value for iterators is the idiomatic way
  • const-correctness of begin()/end()
  • overload begin() / end() for non-const iterators and also provide cbegin()/cend()

In general it's best to use the same interface (constness, return value) as the function you are wrapping (map's begin()/end() in this case).

TemplateRex
  • 69,038
  • 19
  • 164
  • 304
  • Thanks for the help. Yeah, I got into C++11 also, I like the new features, but as I use VS2010, I lacks of some features. – newhouse Jan 10 '13 at 09:24
  • I'm getting some weird HTML bugs with balancing < and > so that the code doesn't layout properly – TemplateRex Jan 10 '13 at 09:25
  • @rhalbersma That's because your code block followed a list element directly, so the indentation was interpreted differently by Markdown. You could have added "Code:" before it and it would work as usual. – Kos Jan 10 '13 at 09:26