跳转至

09 函数模板⚓︎

609 个字 52 行代码 预计阅读时间 4 分钟

函数模板⚓︎

无论是前面讲到的类模板,还是接下来要讲的函数模板(template function,它们都是 C++ 泛型(generic)编程的重要形式,都是将数据类型进行参数化。函数模板是一种泛型函数(generic function

虽然我们前面提到的函数重载(function overloading)也能实现一个函数的多种版本,但是编写起来有些麻烦(复制粘贴 + 修改类型 + 有时还得稍微转化一下。而函数模板不仅能够实现这个功能,还不用重复写那么多遍的代码,只要一个模板即可。下面是一个简单的函数模板,与类模板相似:

template <typename Type>
Type myMin(Type a, Type b)
{
    return a < b ? a : b;
}

其中第一行可以设置默认参数类型:

template <typename Type=int>

调用模板函数时,我们既可以显式指出需要传递的参数类型,比如:

cout << myMin<int>(3, 4) << endl;

也可以隐式调用,让编译器自行推断,比如:

template <typename T, typename U>
auto smarterMyMin(T a, U b)
{
    return a < b ? a : b;
}

// in the main() function
cout << smarterMyMin<int>(3.2, 4) << endl;

和类模板相似,直到被使用前,函数模板不会被编译,编译器会根据实例化后(调用时)的参数类型来决定生成什么样的函数模板的具体版本,编译后生成的代码就像我们平时写的一般函数。这样代码的运行效率会有所提高。

约束和概念⚓︎

C++ 20 及以上的版本中,我们可以限制类模板、函数模板,以及类模板的非模板成员函数的可接受的类型,这样的限制被称为约束(constraints,这些约束的名称集合被称为概念(concept

// Concept
template<typename T>
concept Addable = requires (T a, T b)
{
    a + b;
};

// Constainted template function
// template<typename T> requires Addable<T>
// T add(T a, T b) { return a + b; }

// A shorthand
template <Addable T> T add(T a, T b){ return a + b; }

还没完全理解,待补充

模板元编程⚓︎

通常,代码在运行期间(runtime)内运行;而在模板元编程中,代码能够在编译期间(compile time)运行一次,这样能够提升代码运行的性能和效率。下面是模板元编程的一个🌰

template<unsigned n>
struct Factorial
{
    enum { value = n * Factorial<n - 1>::value };
};

template<>
struct Factorial<0>
{
    enum { value = 1 };
};

std::cout << Factorial<10>::value << endl;

constexpr⚓︎

另外一种能让 C++ 在编译期间运行代码的方法是使用关键词 constexpr 来指明一个常量表达式。

  • 常量表达式必须立即进行初始化,且在编译期间运行
  • 传入常量表达式内的蚕食也应该是常量或者常量表达式
  • 变量可悲声明为 constexpr

相比第一种方法,constexpr 的模板元编程更加可读,还是接着之前的例子进行修改:

constexpr double fib(int n)
{
    if (n == 1) return 1;
    return fib(n - 1) * n;
}

int main()
{
    const long long bigval = fib(20);
    std::cout << bigval << std::endl;
}

评论区

如果有什么问题或想法,欢迎大家在下方留言~