功能模板重载

时间:2010-02-01 02:16:31

标签: c++ templates overloading

任何人都可以总结一下函数模板重载的概念吗?重要的是,模板参数或功能参数?返回值怎么样?

例如,给定一个功能模板

template<typename X, typename Y> void func(X x, Y y) {}

什么是重载的功能模板?

1) template<typename X> void func(X x, int y) {}
2) template<typename X, typename Y> X func(X x, Y y) {}
3) template<class X, class Y, class Z> void func(X x, Y y, Z z) {}

5 个答案:

答案 0 :(得分:27)

在该列表中只有第二个引入歧义,因为函数 - 无论它们是否是模板 - 都不能基于返回类型重载。

您可以使用其他两个:

template<typename X> void func(X x, int y);
如果调用的第二个参数是int,则将使用

,例如func("string", 10);

template<class X, class Y, class Z> void func(X x, Y y, Z z);
如果用三个参数调用func,将使用


我不明白为什么其他一些答案提到模板函数和函数重载不混合。他们肯定会这样做,并且有一些特殊的规则如何选择调用函数。

<强> 14.5.5

  

功能模板可以   超载其他功能   模板和正常   (非模板)功能。一般   功能与a无关   功能模板(即永远不会   被认为是专业化),   即使它具有相同的名称和类型   作为潜在生成的函数   模板专业化。)

非模板化(或“模板化程度较低”)过载优于模板,例如

template <class T> void foo(T);
void foo(int);

foo(10); //calls void foo(int)
foo(10u); //calls void foo(T) with T = unsigned

使用一个非模板参数的第一次重载也属于此规则。

在多个模板之间进行选择时,首选更专业的匹配:

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

int i;
int* p;
int arr[10];

foo(i);  //calls first
foo(p);   //calls second
foo(arr); //calls second: array decays to pointer

您可以在标准的同一章中找到所有规则的更正式的描述(功能模板


最后,在某些情况下,两个或多个重载将是不明确的:

template <class T> void foo(T, int);
template <class T> void foo(int, T);

foo(1, 2);

这里的电话不明确,因为两位候选人都是同样专业的。

您可以使用(例如)boost::disable_if消除此类情况的歧义。例如,我们可以指定当T = int时,不应将第二个重载包含为重载候选:

#include <boost/utility/enable_if.hpp>
#include <boost/type_traits/is_same.hpp>
template <class T>
void foo(T x, int i);

template <class T>
typename boost::disable_if<boost::is_same<int, T> >::type
foo(int i, T x);

foo(1, 2); //calls the first

这里的库在第二次重载的返回类型中产生“替换失败”,如果T = int,则将其从重载候选集中删除。

在实践中,你应该很少遇到这样的情况。

答案 1 :(得分:3)

这里有两个独立的东西:函数模板和函数重载。任何两个不同的模板声明都可能是彼此的重载,因此您的问题并不像所述的那样有意义。 (你提供的三个“重载”不是建立在第一个模板上,而是对同一个函数名称有四个重载。)真正的问题是,给定一些重载和一个调用,如何调用期望的超载?

首先,无论是否涉及模板,返回类型都不参与重载过程。所以#2永远不会与#1一起发挥。

其次,函数模板重载决策的规则与更常用的类模板特化规则不同。两者基本上解决了同样的问题,但

  • 类模板的规则更简单,更强大,允许例如递归,而(成员)函数只有返回类型不同
  • 函数模板的规则允许编译器从函数参数类型
  • 中计算模板参数

您可能能够通过函数模板重载来解决您的特定问题,但是您可能无法修复由于规则较长且较少人熟悉其复杂性而导致的任何错误。经过几年的模板黑客攻击后,我才意识到微妙的功能模板重载甚至是可能的。在像Boost和GCC的STL这样的图书馆中,一种替代方法无处不在。使用模板化的包装类:

template< typename X, typename Y >
struct functor {
    void operator()( X x, Y y );
};
template< typename X > // partial specialization: 
struct functor< X, int > { // alternative to overloading for classes
    void operator()( X x, int y );
};

现在你牺牲了隐式实例化语法(没有尖括号)。如果你想得到它,你需要另一个功能

template< typename X, typename Y > void func( X x, Y y ) {
    return functor< X, Y >()( x, y );
}

我很想知道函数重载是否可以做任何事情(除了演绎)类[部分]专业化不能...

然后,当然,你的重载#3永远不会面临歧义,因为它的参数数量不同于任何其他重载。

答案 2 :(得分:1)

我有所纠正 - 请参阅下面的评论。我不会更改任何原始帖子,因为这会删除响应的上下文。我感谢评论者的投入,并且非常友好,不要投票给我。


考虑将模板化为宏预处理器,它在编译器看到之前扩展#defines。

编译器将“扩展”您的模板参数,然后查看您的函数声明。所以,template parameter == function parameter。如果两次声明相同的函数,则会出错。

您询问退货类型。这是函数'签名'的一部分。具有相同参数但返回类型不同的两个函数是两个不同的函数。

答案 3 :(得分:1)

除了评论之外,还有一些关于Herb Sutters文章Why Not Specialize Function Templates中主题的更多信息。希望它也会有所帮助。

答案 4 :(得分:0)

void fun(int i){
    cout<<i<<endl;
}

void fun(int i, string str){
    cout<<i<<str<<endl;
}

template <typename ... Args>
void sender(Args&& ... args)
{
    fun(forward<Args>(args) ...);
}

int main(){
    sender(5, "hello");
    sender(7);
}