C++模板元编程实战练习题
基于《C++模板元编程入门与实战》的巩固练习,通过三个不同难度的题目,深入掌握编译期计算、类型萃取和模板特化的核心技术。
练习题概览
| 题目 | 难度 | 核心技能 | 预计完成时间 |
|---|---|---|---|
| 1. 通用向量点积计算 | 基础 | 函数模板、类模板、模板实例化 | 15分钟 |
| 2. 编译期快速幂算法 | 进阶 | 模板递归、constexpr、编译期计算 | 25分钟 |
| 3. 类型安全的容器适配器 | 挑战 | 类型萃取、SFINAE、模板特化 | 35分钟 |
环境要求
- 编译器:支持C++17标准的g++或clang++
- 编译命令:
g++ -std=c++17 -o solution solution.cpp - 建议在Linux/macOS环境下测试,确保所有static_assert验证通过
题目一:通用向量点积计算(基础题)
题目描述
在数学和物理中,向量的点积(内积)是一个基本运算。你需要实现一个通用的点积计算模板,能够处理不同维度、不同类型的向量(如int、float、double等)。
具体要求
- 实现一个函数模板
dotProduct,接受两个相同长度的数组(向量),返回它们的点积结果。 - 实现一个类模板
Vector,封装固定长度的向量,并提供一个成员函数dot计算与另一个向量的点积。 - 确保代码类型安全:两个向量必须长度相同、元素类型可进行乘法运算。
- 编写测试代码,验证
int、float类型向量的点积计算。
解题思路提示
- 函数模板可以使用模板参数推导数组长度(通过
std::size或模板非类型参数)。 - 类模板可以包含一个静态数组作为数据成员,长度作为模板非类型参数。
- 考虑使用
auto作为返回类型,让编译器推导结果类型(防止溢出)。 - 使用
static_assert确保两个向量长度相同。
解决方案代码
#include <iostream>
#include <type_traits>
#include <cstddef>
// ==================== 方案一:函数模板 ====================
/**
* 通用点积计算函数模板
* @tparam T 元素类型
* @tparam N 向量长度(通过模板非类型参数指定)
* @param a 第一个向量
* @param b 第二个向量
* @return 点积结果(类型为T)
*/
template<typename T, std::size_t N>
constexpr T dotProduct(const T (&a)[N], const T (&b)[N]) {
T result = T(); // 零初始化
for (std::size_t i = 0; i < N; ++i) {
result += a[i] * b[i];
}
return result;
}
// 替代方案:使用指针和长度参数(更灵活)
template<typename T>
constexpr T dotProduct(const T* a, const T* b, std::size_t n) {
T result = T();
for (std::size_t i = 0; i < n; ++i) {
result += a[i] * b[i];
}
return result;
}
// ==================== 方案二:类模板 ====================
/**
* 通用向量类模板
* @tparam T 元素类型
* @tparam N 向量维度
*/
template<typename T, std::size_t N>
class Vector {
T data[N]; // 静态数组存储向量元素
public:
// 默认构造函数
Vector() = default;
// 初始化列表构造函数
Vector(std::initializer_list<T> init) {
std::size_t i = 0;
for (const auto& val : init) {
if (i < N) data[i++] = val;
}
}
// 获取向量维度(静态版本,便于编译期使用)
static constexpr std::size_t size() {
return N; }
// 下标访问(读写)
T& operator[](std::size_t index) {
return data[index]; }
const T& operator[](std::size_t index) const {
return data[index]; }
/**
* 计算与另一个向量的点积
* @tparam U 另一个向量的元素类型(允许不同类型但可转换)
* @param other 另一个向量
* @return 点积结果(类型为decltype(data[0] * other[0]))
*/
template<typename U>
auto dot(const Vector<U, N>& other) const {
// 编译期检查:确保两个向量长度相同
static_assert(N == Vector<U, N>::size(), "Vectors must have same dimension");
decltype(data[0] * other[0]) result = {
}; // 自动推导结果类型
for (std::size_t i = 0; i < N; ++i) {
result += data[i] * other[i];
}
return result;
}
// 打印向量
void print(const char* name = "") const {
std::cout << name << " = [";
for (std::size_t i = 0; i < N; ++i) {
std::cout << data[i];
if (i < N - 1) std::cout << ", ";
}
std::cout << "]" << std::endl;
}
};
// ==================== 测试代码 ====================
int main() {
std::cout << "=== 题目一:通用向量点积计算测试 ===\n\n";
// 测试1:函数模板(数组版本)
{
constexpr int a1[] = {
1, 2, 3};
constexpr int b1[] = {
4, 5, 6};
int result1 = dotProduct(a1, b1);
std::cout << "测试1 - 函数模板(int数组):\n";
std::cout << " dot([1,2,3], [4,5,6]) = " << result1 << std::endl;
static_assert(sizeof(a1)/sizeof(a1[0]) == 3, "数组长度应为3");
}
// 测试2:函数模板(指针版本)
{
constexpr float a2[] = {
1.5f, 2.5f};
constexpr float b2[] = {
2.0f, 3.0f};
float result2 = dotProduct(a2, b2, 2);
std::cout << "\n测试2 - 函数模板(float指针):\n";
std::cout << " dot([1.5,2.5], [2.0,3.0]) = " << result2 << std::endl;
}
// 测试3:类模板
{
Vector<int, 3> v1 = {
1, 2, 3};
Vector<int, 3> v2 = {
4, 5, 6};
auto result3 = v1.dot(v2);
std::cout << "\n测试3 - 类模板(同类型):\n";
v1.print("v1");
v2.print("v2");
std::cout << " v1·v2 = " << result3 << std::endl;
}
// 测试4:混合类型(int与float)
{
Vector<int, 2> v3 = {
2, 3};
Vector<float


4445

被折叠的 条评论
为什么被折叠?



