C++模板基础(三)

时间:2022-06-19 01:11:10

函数模板(三)
● 模板实参并非总是能够推导得到
– 如果模板形参与函数形参无关,则无法推导

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

C++模板基础(三)

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

C++模板基础(三)

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

C++模板基础(三)

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++基础与深度解析