函数模板(三)
● 模板实参并非总是能够推导得到
– 如果模板形参与函数形参无关,则无法推导
template<typename T, typename Res>
Res fun(T input) //无法推导函数模板的返回类型
{
std::cout << input << std::endl;
}
int main(int argc, char *argv[])
{
QCoreApplication a(argc, argv);
fun(3); //Error: No matching function for call to 'fun'
return a.exec();
}
– 即使相关,也不一定能进行推导,推导成功也可能存在因歧义而无法使用
template<typename T>
void fun(unsigned input = sizeof(T)) //推导时不会考虑缺省值
{
std::cout << input << std::endl;
}
int main(int argc, char *argv[])
{
QCoreApplication a(argc, argv);
fun(3); //Error: No matching function for call to 'fun'
return a.exec();
}
#include <type_traits>
template<typename T>
void fun(typename std::remove_reference<T>::type input = sizeof(T)) //remove_reference: 移除引用
{
std::cout << input << std::endl;
}
int main(int argc, char *argv[])
{
QCoreApplication a(argc, argv);
fun(3); //Error: No matching function for call to 'fun'
return a.exec();
}
● 在无法推导时,编译器会选择使用缺省模板实参
#include <type_traits>
template<typename T = int> //int是缺省模板实参
void fun(typename std::remove_reference<T>::type input = sizeof(T)) //remove_reference: 移除引用
{
std::cout << input << std::endl;
}
int main(int argc, char *argv[])
{
QCoreApplication a(argc, argv);
fun(3); //OK
return a.exec();
}
#include <type_traits>
template<typename T = int> //但是有缺省模板实参
void fun(unsigned input = sizeof(T)) //无法通过unsigned input = sizeof(T)推导模板实参
{
std::cout << input << std::endl;
}
int main(int argc, char *argv[])
{
QCoreApplication a(argc, argv);
fun(3); //OK
return a.exec();
}
template<typename T, typename Res = int> //有缺省模板实参
Res fun(T input)
{
std::cout << input << std::endl;
return input;
}
int main(int argc, char *argv[])
{
QCoreApplication a(argc, argv);
fun(3); //OK
return a.exec();
}
template<typename T = double> //有缺省模板实参
void fun(T input1, T input2)
{
std::cout << input1 + input2 << std::endl;
}
int main(int argc, char *argv[])
{
QCoreApplication a(argc, argv);
fun(3, 5.0); //可以推导模板实参,但推导的结果有歧义
return a.exec();
}
– 可以为任意位置的模板形参指定缺省模板实参 注意与函数缺省实参的区别
void fun(int x = 5, int y) //Error::Missing default argument on parameter 'y'
{
}
template<typename Res = double, typename T>
Res fun(T input1, T input2)
{
std::cout << input1 + input2 << std::endl;
return input1;
}
int main(int argc, char *argv[])
{
QCoreApplication a(argc, argv);
fun(3, 5); //OK
return a.exec();
}
template<typename T1, typename Res = double, typename T2>
Res fun(T1 input1, T2 input2)
{
std::cout << input1 + input2 << std::endl;
return input1;
}
int main(int argc, char *argv[])
{
QCoreApplication a(argc, argv);
fun(3, 5); //OK: T1, T2可以推导出来,Res有缺省值
return a.exec();
}
● 显式指定部分模板实参
– 显式指定的模板实参必须从最左边开始,依次指定
template<typename T1, typename Res = double, typename T2>
Res fun(T1 input1, T2 input2)
{
std::cout << input1 + input2 << std::endl;
return input1;
}
int main(int argc, char *argv[])
{
QCoreApplication a(argc, argv);
fun<int, int>(3, 5); //OK: 第一个int和T1关联,第二个int和Res关联
return a.exec();
}
template<typename T, typename Res>
Res fun(T input)
{
return input;
}
int main(int argc, char *argv[])
{
QCoreApplication a(argc, argv);
fun<int, int>(3); //OK: 第一个int和T1关联,第二个int和Res关联
return a.exec();
}
– 模板形参的声明顺序会影响调用的灵活性
template<typename Res, typename T>
Res fun(T input)
{
return input;
}
int main(int argc, char *argv[])
{
QCoreApplication a(argc, argv);
fun<int>(3); //OK: int和Res关联,T可以推导出来
return a.exec();
}
● 函数模板自动推导时会遇到的几种情况
– 函数形参无法匹配—— SFINAE (替换失败并非错误)
template<typename T>
void fun(T input1, T input2)
{
}
int main(int argc, char *argv[])
{
QCoreApplication a(argc, argv);
fun(3, 5.0); //Error: main.cpp:12:5: No matching function for call to 'fun'. Candidate template ignored: deduced conflicting types for parameter 'T' ('int' vs. 'double')
return a.exec();
}
template<typename T>
void fun(T input1, T input2)
{
}
template<typename T1, typename T2>
void fun(T1 input1, T2 input2)
{
}
int main(int argc, char *argv[])
{
QCoreApplication a(argc, argv);
fun(3, 5.0); //OK
return a.exec();
}
– 模板与非模板同时匹配,匹配等级相同,此时选择非模板的版本
template<typename T1, typename T2>
void fun(T1 input1, T2 input2)
{
std::cout << "template<typename T1, typename T2> void fun(T1 input1, T2 input2)\n";
}
void fun(int x, double y)
{
std::cout << "void fun(int x, double y)\n";
}
int main(int argc, char *argv[])
{
QCoreApplication a(argc, argv);
fun(3, 5.0); //涉及到重载解析
return a.exec();
}
template<typename T1, typename T2>
void fun(T1 input1, T2 input2)
{
std::cout << "template<typename T1, typename T2> void fun(T1 input1, T2 input2)\n";
}
void fun(int x, double y)
{
std::cout << "void fun(int x, double y)\n";
}
int main(int argc, char *argv[])
{
QCoreApplication a(argc, argv);
fun(3, 5.0f); //与函数模板完美匹配,与函数带有转换的匹配
return a.exec();
}
– 多个模板同时匹配,此时采用偏序关系确定选择 最特殊 的版本
template<typename T1, typename T2>
void fun(T1 input1, T2 input2) //T2可以对应double, int, float等等
{
std::cout << "template<typename T1, typename T2> void fun(T1 input1, T2 input2)\n";
}
template<typename T1>
void fun(T1 input1, float input2) //float只能是float,该版本较特殊
{
std::cout << "template<typename T1> void fun(T1 input1, float input2)\n";
}
int main(int argc, char *argv[])
{
QCoreApplication a(argc, argv);
fun(3, 5.0f);
return a.exec();
}
template<typename T>
void fun(T x)
{
std::cout << "template<typename T> void fun(T x)\n";
}
template<typename T>
void fun(T* x) //明确指出参数是T类型的一个指针,该版本较为特殊
{
std::cout << "template<typename T> void fun(T* x)\n";
}
int main(int argc, char *argv[])
{
QCoreApplication a(argc, argv);
int x = 3;
fun(&x);
return a.exec();
}
template<typename T1, typename T2>
void fun(T1 x, T2* y)
{
std::cout << "template<typename T1, typename T2> void fun(T1 x, T2* y)\n";
}
template<typename T1, typename T2>
void fun(T1* x, T2 y) //明确指出参数是T类型的一个指针,该版本较为特殊
{
std::cout << "template<typename T1, typename T2> void fun(T1* x, T2 y)\n";
}
int main(int argc, char *argv[])
{
QCoreApplication a(argc, argv);
int x = 3;
fun(&x, &x); //Error: Call to 'fun' is ambiguous
return a.exec();
}
参考
深蓝学院:C++基础与深度解析