C++中的模板静态成员函数

4
我写了一个简单的测试程序,尝试学习如何在C++中使用模板静态成员函数。代码编译通过,但不能正常工作(输出一些垃圾值)。我猜测我使用了正确的语法。我已经阅读了这篇文章或者这篇文章和其他一些内容,但仍然不知道我做错了什么。以下是代码:
#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;
}

任何提示都会受到赞赏。

4个回答

5

您需要将大小作为另一个模板参数传递:

#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);
}

@hackworks 更好的方法是使用范围(将数组的开头和结尾传递给函数)。然而,这不是被要求的。 - BЈовић
内联在这里没有帮助。编译器将每个具有不同N的printTad()调用视为不同的函数,并对每个函数执行模板扩展。 - deepsnore
@hackworks template <typename T,int N> inline static void printTab(T (&tab)[N]) { printTab(tab, N); } 这个函数是你的重载。没有额外开销。 - Pubby
在两种方法中增加一些调用printTab()函数的代码,其中N是模板参数和非模板参数。运行以下命令并查看差异:“nm a.out | grep pringTab | wc -l”。在我的实现中,您将始终看到2,并且使用模板参数时它会随不同的N值而增加。 - deepsnore
@mmm 这个函数接受一个大小为N的数组引用。例如,看这里:http://www.cplusplus.com/forum/general/4125/#msg18176 - BЈовић
显示剩余3条评论

2

sizeof(tab)返回的是T*类型的大小,它不会返回整个数组的大小。你需要将整个数组的大小作为另一个参数传递给函数。如果想了解更多信息和其他潜在的解决方法,请参考这里:当一个函数有一个特定大小的数组参数时,为什么会被替换成指针?

请注意,第二个printTab将不会输出可读字符。如果想要看到输出结果,请尝试使用:

 unsigned char tabChar[3] {'1', '2', '3'};

谢谢Mat。我已经更改了printTab的实现方式: cout << "sizeof(T):" << sizeof(T) << "; sizeof(tab): " << sizeof(tab) << endl; 用于测试,它输出: 对于float:sizeof(T):4; sizeof(tab): 8 对于char:sizeof(T):1; sizeof(tab): 8 我不太理解这个结果,但至少我知道问题出在哪里了。 - mmm

1

不妨试试,在调用函数时需要发送数组的大小:

#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
有更安全的宏来计算数组的大小,请参见我的答案,无论如何,这是绕过模板安全性并引入错误风险相当奇怪的做法。 - Matthieu M.
@MatthieuM。当“N”成为模板参数时,编译器不会为不同的“N”值生成额外的代码吗? - deepsnore
这要看情况。对于宏(C++03 兼容),不会有影响。请注意,该函数从未被定义过,因此我们只能在未求值的上下文中使用它(sizeofdecltype 等)。没有定义,也就没有代码。至于结构体,没有虚方法,因此也没有 RTTI 信息。对于 constexpr 函数,可能会有影响。但是,这个函数是内联的最佳选择,所以一旦你开始优化(O1 及以上),它 应该 被内联。通过将其设置为 static,可以消除定义,一旦内联后就不再被引用。 - Matthieu M.
1
但问题是,这真的重要吗?这样一个函数只有几个字节。几个从未读取过的内存字节。这是为了永远不会出现大小错误而付出的便宜代价,肯定比追踪缓冲区溢出要便宜得多。 - Matthieu M.
@MatthieuM。我完全同意你的观点。我只是在关注细节以便更多地学习。在我工作的地方,我们非常注重二进制文件大小,因为它需要被压缩到可启动的闪存卡中。因此,我们都很谨慎。感谢你的好解释。 - deepsnore

-1
我会将T的元素数量作为函数参数传递或使用STD容器,例如Vector。
你的for循环只打印第一个元素tab[0]而不是tab[i]。
你的tabFloat和tabChar的初始化缺少=。
float tabFloat[5] {1, 2, 3, 4, 5}; 
unsigned char tabChar[3] {1, 2, 3};

(在测试中,我建议使用65、66、67代替1、2、3以提高控制台的可读性)

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

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接