6

I have here this code.

#include <iostream>
using namespace std;

template <typename T> inline T bigArry(const T data[5])
{
    T level = data[0];
    for(T item : data) // error C2143: syntax error : missing ',' before ':' (1st)
    { //error C2143: syntax error : missing ';' before '{' (3rd)
        if(level<item){ level=item; }
    }
    return  level;
}

int main()
{
    int data[5]={//five variables}
    cout << bigArry(data);//see reference to function template instantiation 'T bigArry<int>(const T [])' being compiled with [ T=int] (2nd)

    return 0;
}

The function bigArry() returns the highest value out of a array of 5 elements.

The problem is that when I use the range-based loop it gives me the errors mentioned in the code. But when I use the usual for, everything goes back to normal. I mean, the syntax to me looks fine, I can't see the problem. I'm using Visual Studio 2010.

The other thing I want to ask is about the inline functions. Currently I'm reading C++ Primer Plus 6th edition. When do I know when a function is too big to be inlined? Is there a standard of how short the code should be? Or, do we use the inline functions when we "think" it's okay?

Robert Lucian Chiriac
  • 686
  • 2
  • 15
  • 24

2 Answers2

7

The parameter data is NOT an array in your function template. It is actually a pointer.

This function

template <typename T> inline T bigArry(const T data[5])

is exactly same as this:

template <typename T> inline T bigArry(const T *data)

There is NO difference at all.

That is the reason why your code gives compilation error.

There're couple of fixes here:

  • You could accept the argument by reference, as:

    template <typename T> 
    inline T bigArry(const T (&data)[5]) //NOTE &
    

    That should work. But then that looks cumbersome. Maybe, use the next.

  • Or you could use this (as suggested by @yzt):

    template <typename C> 
    inline auto bigArry(C const & data) -> decltype(data[0])
    

    This is clean as well as more flexible than the above one (and the below one). In addition to passing arrays, you could pass any container as long as data[0] is well-defined and means what it supposed to mean.

  • Or if you wish, you could use std::array<T, 5> as:

    template <typename T> 
    inline T bigArry(const std::array<T,5> & data) 
    

Hope that helps.

Nawaz
  • 353,942
  • 115
  • 666
  • 851
  • I believe this signature will work with plain C arrays and some other containers, including `std::vector`s and `std::array`s, and is more permissive in size of arrays that is accepts: `template inline decltype(C[0]) bigArry(C const & data)` – yzt Jun 29 '13 at 16:49
  • I suggest it here (instead of in another answer) for you to add it to your answer, if you see fit. I think it's a better answer than using `std::array` as it doesn't require a new header. But it's certainly more "cumbersome". – yzt Jun 29 '13 at 17:02
  • @yzt: Done. (BTW, `decltype(C[0])` doesn't mean anything; what you meant is called trailing-return-type). – Nawaz Jun 29 '13 at 17:14
  • Oh, right! What I meant to write was `decltype(C()[0])`, but that's incorrect too. The correct way, as you point out, is a trailing `-> decltype(data[0])`. Thanks. – yzt Jun 29 '13 at 23:26
6

That is because array types decay to pointers when used as function parameters or when passed as function argument. In other words, your function signature is equivalent to:

template <typename T> inline T bigArry(const T* data)

The range-based for loop passes data to the global std::begin() and std::end() functions in order to get iterators to (respectively) the first and the one-past-the-last element of the container.

Of course, there are no global std::begin() and std::end() functions that accept a pointer, and they couldn't be meaningfully defined either: how to determine the end of the container given just a pointer to its first element?

You can use std::array instead of C arrays (std::array is a zero-overhead wrapper around a C array), and modify your calling function accordingly:

template <typename T> inline T bigArry(std::array<T, 5> data)
//                                     ^^^^^^^^^^^^^^^^
{
    T level = data[0];
    for(T item : data)
    {
        if(level<item){ level=item; }
    }
    return  level;
}

int main()
{
    std::array<int, 5> data = {1,2,3,4,5};
//  ^^^^^^^^^^^^^^^^^^^^^^^
    std::cout << bigArry(data);

    return 0;
}

Here is a live example.

Andy Prowl
  • 124,023
  • 23
  • 387
  • 451
  • I believe this signature will work with plain C arrays and some other containers, including `std::vector`s and `std::array`s: `template inline decltype(C[0]) bigArry(C const & data) {...}` – yzt Jun 29 '13 at 16:46
  • @Andy: Actually [the rules for the curly-braces](http://stackoverflow.com/questions/11734861/when-can-outer-braces-be-omitted-in-an-initializer-list) are a bit confusing at times. But what I said before doesn't apply in this case. – Nawaz Jun 29 '13 at 16:49
  • I'm using the curly-braces that way so I can work with the concept that everything is clustered. It's easier for me. – Robert Lucian Chiriac Jun 29 '13 at 16:59
  • @AndyProwl: I suggest it here (instead of in my own answer) for you to add it to your answer, if you see fit. I think it's a better solution than using `std::array` as it doesn't require a new header. – yzt Jun 29 '13 at 17:03
  • By the way, I made a mistake in my first comment (as pointed out by @Nawaz in another answer's comment section.) The hopefully correct way is `template inline auto bigArry(C const & data) -> decltype(data[0]) {...}`. – yzt Jun 29 '13 at 23:28