7
#include <iostream>
template <class T> 
void foo(T) {
    std::cout << "foo(T)" << std::endl;
}

template <class T> 
void foo(T*) { //#3
    std::cout << "foo(T*)" << std::endl;
}

#define TEST

#ifdef TEST
template <> 
void foo(int*) { //#1
    std::cout << "foo(int*)" << std::endl;
}
#else
template <>
void foo<int*>(int*) { //#2
    std::cout << "foo<int*>(int*)" << std::endl;
}
#endif

int main(int argc, char **argv) {
    int* p = 0;
    foo(p);
    return 0;
}

#1 和 #2 有什么区别。如果我定义测试,#1 工作。但如果我评论它,#3 工作......这是编写函数模板专业化的正确方法......

4

3 回答 3

3

#1 声明了 #3 的函数模板特化,并自动推导出模板参数。#2 是您为T=int*. 它不能是 #3 的特化,因为用T指定的替换int*会导致int**参数。

当您调用 时foo,重载解析现在首先选择最合适的基本模板,然后检查该模板是否存在任何现有特化。定义后,有TEST两个基本模板(#0 和#3),#3 是更好的匹配并被选中。然后编译器检查该模板的特化,#1 更合适并被调用。

如果没有TEST定义,仍然有两个基本模板(#0 和#3),#3 是更好的匹配并被选中。然后编译器检查该模板的特化,但由于#2 特化#0 而不是#3,因此不考虑它并且#3 结束被调用。

这是Why not Specialize Function Templates的经典示例。那里更详细地解释了这些问题。

简单的解决方案是根本不专门化函数模板,而只是为特殊类型添加新的重载:

// no template, just a normal function
void foo(int*) {
    std::cout << "foo(int*)" << std::endl;
}
于 2012-01-21T12:19:03.380 回答
2

对于函数模板特化,您可以显式列出模板参数,但如果模板参数是推导的,则不必这样做。如果您不指定模板参数,它们将由编译器使用与重载解析相同的规则推导出来。为了决定选择哪个函数重载,编译器首先只查看主模板(首先由一些神奇的过程选择)。查看两个可用的主模板

template <typename T> void foo(T);
template <typename T> void foo(T*);

后者更适合指针参数。一旦找到合适的主模板,编译器就会寻找这个主模板的潜在特化。但是,您的示例 #2 实际上不是采用指针参数的函数模板的特化,尽管它涉及指针参数。如果您采取主要声明

template <typename T> void foo(T*);

然后你用你得到T的明确指定的模板参数替换int*

template <> void foo<int*>(int**);

也就是声明

template <> void foo<int*>(int*);

是不同的东西。您可能只想在指定模板参数时丢失指针:

template <> void foo<int>(int*);
于 2012-01-21T12:10:19.537 回答
0

我无法真正说出#2 应该专门化哪个函数,或者确切地说极其复杂的重载解析规则将如何选择要调用的函数。

我知道您通常不需要专门化函数,而是可以依赖重载。要获得一个功能,int*你只需要

void foo(int*) {
    std::cout << "foo(int*)" << std::endl;
}

只要参数匹配,非模板函数将优于模板。

于 2012-01-21T12:09:00.497 回答