众所周知,功能模板不能部分专门用于C语言 . 当您在概念上尝试实现此目标时,您可以使用两种可能的解决方案 . 其中一个是使用带有静态函数的结构,可选地用模板函数包装,如下所示:
template <class T, class U>
struct BarHelper
{
static void BarHelp(T t, const U& u)
{
std::cerr << "bar general\n";
}
};
template <class T>
struct BarHelper<T, double>
{
static void BarHelp(T t, const double& u)
{
std::cerr << "bar specialized\n";
}
};
template <class T, class U>
void bar(T t, const U& u)
{
BarHelper<T, U>::BarHelp(t, u);
};
bar
这里是可选的,你可以直接使用struct的静态成员(尽管你必须明确指定所有参数) .
另一种方法是重载函数模板:
template <class T, class U>
void func(T t, const U& u)
{
std::cerr << "func general\n";
}
template <class T>
void func(T t, const double& u)
{
std::cerr << "func specialized\n";
}
对我来说,似乎第二种方法更可取 . 对于初学者来说,它更加冗长,而且对于意图更加清晰(我们正在编写函数,所以让我们使用函数而不是无意义的包装器结构) . 此外,您可以使用一些很好的技巧来控制重载分辨率 . 例如,您可以在继承层次结构中包含非模板化的“标记”参数,并使用隐式转换来控制函数的优先级 . 每当你在重载中具体指定一个类型时,你也会得到隐式转换,如果你不喜欢这种行为,你可以在你的重载上使用enable_if来阻止它(让你回到与结构相提并论) .
是否有理由偏爱部分专业结构?这些原因有多普遍?即哪个应该是你的“默认”?如果您:a)计划自己实现所有特化,而b)这是否用作用户可以注入自己行为的自定义点,这会有所不同吗?
Herb Sutter有一篇关于避免功能模板专业化的着名博文 . 在其中,他还建议(接近结尾)更喜欢部分专门的结构来重载函数模板,但他似乎没有给出任何具体的理由:http://www.gotw.ca/publications/mill17.htm .
道德#2:如果你正在编写一个函数库模板,宁愿把它写成一个永远不应该专门化或过载的单个函数模板
(重点补充) .
1 回答
Let's list first the options for creating several variants of the same template method:
Simple overloading: 这可行, as the question mentions and demonstrates .
但是,它并不总是很好,我们将在下面看到 .
Using functor class partial specialization: 这是没有模板功能专业化的直接替代方案 .
Using std::enable_if along with template functions overloading: 当简单模板重载不起作用时,可以选择此方法,请参见下文 .
该问题提出了一种情况,当模板参数从调用中推导出来时,模板函数重载工作正常 . 但是,如果对模板函数的调用直接提供模板参数,并且需要根据模板参数的关系或条件匹配实现,则重载不再有用 .
请考虑以下事项:
虽然在编译时val1和val2是已知的,但是我们无法在编译时知道它们是相同的情况下部分特殊化 . 在这种情况下,函数重载没有帮助,对于两个非类型模板参数具有相同值的情况没有重载 .
通过类部分特化,我们可以做到:
或者,使用
std::enable_if
,我们可以:以上所有选项的主要内容如下:
此选项的主要内容如下:
我认为选项4的语法没有任何优势 . 但是人们可以不这么认为......
注意在选项4中需要一个.cpp文件,对于
T foo::val
的声明,在所有其他选项中,一切都适用于.h文件 .总结:
基于模板元编程,我们可以获得编译时解析的情况,需要部分专业化 . 这可以通过类部分特化或使用enable_if来实现(对于其条件,它又需要其自己的类部分特化) .
见代码:http://coliru.stacked-crooked.com/a/65891b9a6d89e982