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.

4 Answers 4

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);
}
Sign up to request clarification or add additional context in comments.

8 Comments

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
@hackworks If that's problem then inline it and have it call a non-templated function.
@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.
Inline does not help here. Compiler treats each call to printTad() with different N as different function and performs template expansion for each.
@hackworks template <typename T,int N> inline static void printTab(T (&tab)[N]) { printTab(tab, N); } Where the function is your overload. No overhead here.
|
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'};

1 Comment

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.
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;
}

6 Comments

My previous answer was from memory and the current one is from trying the actual code. Care to remove the down vote?
There are more secure macros to compute the size of an array see my answer here, anyway this is quite bizarre to bypass template security and introduce the risk of errors.
@MatthieuM. When 'N' becomes a template parameter, won't compiler generate extra code for different values of 'N'?
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.
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.
|
-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}; 

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.