0

I was trying to print a vector<int> using a helper function as follows:

This doesn't works -

template<class T>
void print(const std::vector<T>& v)
{
    std::vector<T>::const_iterator i;
    for (i = v.begin(); i != v.end(); i++)
        std::cout << *i << "  ";
    std::cout << std::endl;
}

Edit: I get this.

Error

But this works -

template<class T>
void print(const std::vector<T>& v)
{
    // changed std::vector<T> to std::vector<int>
    std::vector<int>::const_iterator i;
    for (i = v.begin(); i != v.end(); i++)
        std::cout << *i << "  ";
    std::cout << std::endl;
}

I wanted to ask following things :

  • Why does first doesn't work and second does?
  • What are alternative ways to write a function for same functionality? P.S. I don't want any of the element to be changed inside the function. I guess it can be done using for_each() algorithm. But I am not sure how I would write the predicate for it.
Martijn Pieters
  • 1,048,767
  • 296
  • 4,058
  • 3,343
Sumit Gera
  • 1,249
  • 4
  • 18
  • 34
  • 1
    Maybe replace `class T` by `typename T` if you want to use `int` – arne Oct 18 '13 at 07:52
  • 6
    Oh, and please try to read the error messages. It actually says exactly what you have to do to fix it. – Some programmer dude Oct 18 '13 at 07:57
  • @arne No, why? They're synonyms in this context. – Angew is no longer proud of SO Oct 18 '13 at 09:12
  • @Angew: They may be synonyms, but it's much, much clearer to users if you only write `class` if you really expect a class type. – arne Oct 18 '13 at 09:15
  • 1
    @arne Honestly, I've been programming template-heavy code for some 4 years and that's the *first time* I hear this distinction. I've learned to favour `class` everywhere, because it cannot be confused with a `typename` signifying a non-type template parameter. – Angew is no longer proud of SO Oct 18 '13 at 09:21
  • 1
    @Angew I've had most of my not-so-template-savvy coworkers ask me whether it would be okay to also instantiate the template with `int` although it said `class`. Some even wrote small wrapper classes just for this purpose before asking. So I decided to go with `typename` if fundamental data types are okay. But that's personal preference and code style, nothing writ in stone. – arne Oct 18 '13 at 09:28
  • @arne Fair enough, this is really up to project policy or coding style. – Angew is no longer proud of SO Oct 18 '13 at 09:35
  • The error message is exceptionally clear. – Puppy Oct 18 '13 at 10:57
  • 2
    Congrats. You made a screenshot of text. –  Oct 18 '13 at 10:57
  • Abrahams & Gurtovoy: "If so, you understand the argument for using `class` to declare template type parameters: It's less confusing if `typename` is only used to mean one thing (syntax disambiguation) in template parameter lists. We're not going to tell you which practice you should use; people of goodwill can disagree about whether conceptual accuracy is more important than avoiding confusion in the rare cases where typename is used in non-type parameter declarations. In fact, the authors of this book disagreed, which is why you'll see `class` here and `typename` in the MPL reference manual." – TemplateRex Oct 18 '13 at 11:06
  • 2
    I prefer to treat my coworkers like grownups and assume the reader knows what they are reading, i.e., knows that `class` and `typename` are interchangeable in that context. If they don't, they have no business reading that code and will go back to reading that book that teaches templates and forever be free of confusion. – R. Martinho Fernandes Oct 18 '13 at 11:19

3 Answers3

4

You should use

typename std::vector<T>::const_iterator i;

to make it work (as the compiler is telling you in the error message).

const_iterator is a template-dependent name in the 1st case, so you have to use the keyword to disambiguate it.

Please follow the link in Joachim Pileborg's comment for a good explanation.

Andriy
  • 8,486
  • 3
  • 27
  • 51
4

std::vector<T>::const_iterator is a dependent name, you need to add typename in front:

typename std::vector<T>::const_iterator i;

Or just write this way:

for (auto it  = v.begin(); it != v.end(); ++it) {
    std::cout << *it << "  ";
}

Or

for (auto const& e : v) 
{
   cout << e << "\n";
}
std::cout << std::endl;

What is dependent name:

A name that depends in some way on a template parameter. Certainly any qualified or unqualified name that explicitly contains a template parameter is dependent. Furthermore, a qualified name that is qualified by a member access operator (. or ->) is dependent if the type of the expression on the left of the access operator depends on a template parameter. In particular, b in this->b is a dependent name when it appears in a template. Finally, the identifier ident in a call of the form ident(x, y, z) is a dependent name if and only if any of the argument expressions has a type that depends on a template parameter.

billz
  • 44,644
  • 9
  • 83
  • 100
  • Or `std::copy(,,std::ostream_iterator(std::cout))` - plenty of ways to do this without needing to spell out the iterator type. – MSalters Oct 18 '13 at 10:51
2
  • Why does first doesn't work and second does ?

Because you neglected the errors

typename std::vector<T>::const_iterator i;
^^^Use typename
  • What are alternative ways to write a function for same functionality? P.S. I don't want any of the element to be changed inside the function. I guess it can be done using for_each() algorithm. But I am not sure how I would write the predicate for it.
struct foo{
    void operator()(const int &i) const{
        std::cout<<i<<"  ";
    }
}
for_each(v.begin(), v.end(),foo());
P0W
  • 46,614
  • 9
  • 72
  • 119
  • So the second works because the `typename` is defined in second function as I explicitly wrote the `type`? – Sumit Gera Oct 18 '13 at 08:02
  • @mozart yes,because in second you have vector of elements of type `int`, so its known – P0W Oct 18 '13 at 08:06