4

I've written a simple test program to try to learn how to use template static member functions in C++. The code compiles, but doesn't work right (prints out some garbage). I guess I'm using the right syntax. I've read this or this and some other stuff but still don't know what I'm doing wrong. The code below:

#include <iostream>
using namespace std;

class Util {
public:
    Util();
    virtual ~Util();

    template <typename T> static void printTab(T tab[]);
};

template <typename T>
void Util::printTab(T tab[]) {
    for (unsigned int i=0; i<sizeof(tab)/sizeof(tab[0]); i++) {
        cout << tab[0] << " ";
    }
    cout << endl;
}

int main() {

    float tabFloat[5] {1, 2, 3, 4, 5};
    unsigned char tabChar[3] {1, 2, 3};

    Util::printTab(tabFloat);
    Util::printTab(tabChar);

    return 0;
}

Any hints appreciated.

Community
  • 1
  • 1
mmm
  • 1,277
  • 5
  • 18
  • 34

4 Answers4

5

You need to pass the size as another template argument :

#include <iostream>
using namespace std;

class Util {
public:
    Util();
    virtual ~Util();

    template <typename T,int N> static void printTab(T (&tab)[N])
    {
        for (int i=0; i<N; i++) {
            cout << tab[i] << " ";
        }
        cout << endl;
    }
};

int main() {

    float tabFloat[5] {1, 2, 3, 4, 5};
    unsigned char tabChar[3] {1, 2, 3};

    Util::printTab(tabFloat);
    Util::printTab(tabChar);
}
BЈовић
  • 62,405
  • 41
  • 173
  • 273
  • making size a template argument is not a good idea. For different values of N, compiler generates new code and this leads to bloated binaries – deepsnore Nov 27 '11 at 14:08
  • @hackworks If that's problem then inline it and have it call a non-templated function. – Pubby Nov 27 '11 at 14:10
  • @hackworks A better way would be to use a range (pass begin and end of an array to the function). However, that's not what is being asked. – BЈовић Nov 27 '11 at 14:12
  • Inline does not help here. Compiler treats each call to printTad() with different N as different function and performs template expansion for each. – deepsnore Nov 27 '11 at 14:13
  • @hackworks `template inline static void printTab(T (&tab)[N]) { printTab(tab, N); }` Where the function is your overload. No overhead here. – Pubby Nov 27 '11 at 14:21
  • Very nice and concise implementation, does exactly what I wanted. If I only understood how it worked... could you please give me any hint about what should I search for on the Internet to read about this solution? I particularly don't understand what is being passed to the function. – mmm Nov 27 '11 at 14:22
  • Add a few more calls to printTab() function in both approaches where N is a template argument and where it is not (my ans). Run the following command and see the difference: "nm a.out | grep pringTab | wc -l". In my implementation, you will see 2 always and with template it increases with different N – deepsnore Nov 27 '11 at 14:29
  • @mmm That function accepts a reference to an array, which has a size of N. For example, take a look here : http://www.cplusplus.com/forum/general/4125/#msg18176 – BЈовић Nov 27 '11 at 14:38
2

sizeof(tab) is the size of a T*, it will not return the size of the whole array. You need to pass that in yourself as another argument to the function. See here for an explanation and another potential workaround: When a function has a specific-size array parameter, why is it replaced with a pointer?

Note that the second printTab will not output readable characters. If you want to see something printed out, try with:

 unsigned char tabChar[3] {'1', '2', '3'};
Community
  • 1
  • 1
Mat
  • 202,337
  • 40
  • 393
  • 406
  • Thanks Mat. I changed printTab's implementation to: cout << "sizeof(T):" << sizeof(T) << "; sizeof(tab): " << sizeof(tab) << endl; for testing, and it outputs: sizeof(T):4; sizeof(tab): 8 for float and sizeof(T):1; sizeof(tab): 8 for char which I don't understand. But at least I know what the problem is. – mmm Nov 27 '11 at 14:10
1

How about trying, you need to send the size of the array when calling a function:

#include <iostream>
using namespace std;

class Util {
public:
    Util();
    virtual ~Util();

    template <typename T> static void printTab(T tab[], size_t sz);
};

template <typename T>
void Util::printTab(T tab[], size_t sz) {
    for (unsigned int i=0; i<sz; i++) {
        cout << tab[i] << " ";
    }
    cout << endl;
}

int main() {

    float tabFloat[5] {1, 2, 3, 4, 5};
    unsigned char tabChar[3] {1, 2, 3};

    Util::printTab(tabFloat, sizeof(tabFloat)/sizeof(float));
    Util::printTab(tabChar, sizeof(tabChar)/sizeof(char));

    return 0;
}
deepsnore
  • 976
  • 5
  • 13
  • My previous answer was from memory and the current one is from trying the actual code. Care to remove the down vote? – deepsnore Nov 27 '11 at 14:05
  • There are more secure macros to compute the size of an array see [my answer here](http://stackoverflow.com/q/8021113/147192), anyway this is quite bizarre to bypass template security and introduce the risk of errors. – Matthieu M. Nov 27 '11 at 17:34
  • @MatthieuM. When 'N' becomes a template parameter, won't compiler generate extra code for different values of 'N'? – deepsnore Nov 28 '11 at 03:08
  • It depends. In the case of the macro (C++03 compatible), it won't. Note that the function is never defined, so we can only really use it in unevaluated contexts (`sizeof`, `decltype`, ...). No definition, no code. As for the structure, there is no virtual method, so no RTTI information either. In the case of `constexpr` function, it may. This function is prime candidate for inling though, so as soon as you start optimizing (`O1` and further) it *should* be inlined. A definition might be emitted, by making it `static` we could probably prune it though: not referenced once inlined. – Matthieu M. Nov 28 '11 at 07:27
  • 1
    But the thing is, does it really matter ? Such a function is only a couple bytes. A couple bytes of never read memory. That's a cheap price to pay for never getting your size wrong, certainly cheaper than tracking down buffer overflows. – Matthieu M. Nov 28 '11 at 07:29
  • @MatthieuM. I completely agree to your points. I am just looking at the details to learn more. We are very conscious on the binary size at the place I work as it needs to be squeezed into a bootable flash card. Hence all the paranoia. Thank you for good explanation. – deepsnore Nov 28 '11 at 07:36
-1

I'd pass the number of elements of T as a function argument or use a STD container such as a Vector.

Your for loop is just printing the first element tab[0] not tab[i]

Your initialization of tabFloat and tabChar are missing =

float tabFloat[5] {1, 2, 3, 4, 5}; 
unsigned char tabChar[3] {1, 2, 3};

(also I'd use 65, 66, 67 instead of 1,2,3 for console readability in your testing)

float tabFloat[5] = {1, 2, 3, 4, 5}; 
unsigned char tabChar[3] = { 65, 66, 67}; 
Doug Moscrop
  • 4,479
  • 3
  • 26
  • 46