【C++篇】领略模板编程的进阶之美:参数巧思与编译的智慧

news2024/9/30 21:57:19

文章目录

  • C++模板进阶编程
    • 前言
    • 第一章: 非类型模板参数
      • 1.1 什么是非类型模板参数?
        • 1.1.1 非类型模板参数的定义
      • 1.2 非类型模板参数的注意事项
      • 1.3 非类型模板参数的使用场景
        • 示例:静态数组的实现
    • 第二章: 模板的特化
      • 2.1 什么是模板特化?
        • 2.1.1 模板特化的分类
      • 2.2 函数模板特化
        • 示例:函数模板的特化
    • 第三章: 类模板特化
      • 3.1 类模板的全特化
        • 示例:全特化
      • 3.2 类模板的偏特化
        • 示例1:部分参数的偏特化
        • 示例2:指针类型的偏特化
      • 3.3 类模板特化的应用示例
        • 示例:对指针进行排序的类模板特化
    • 第四章: 模板的分离编译
      • 4.1 什么是模板的分离编译?
      • 4.2 分离编译中的问题
        • 示例:模板的声明和定义分离
      • 4.3 解决模板分离编译问题
    • 第五章: 模板总结
      • 优点:
      • 缺点:
    • 第六章: 模板元编程(Template Metaprogramming)
      • 6.1 什么是模板元编程?
        • 6.1.1 编译期与运行期的区别
      • 6.2 模板元编程的基础
        • 示例:使用模板元编程计算阶乘
        • 输出:
      • 6.3 使用模板元编程进行条件选择
        • 示例:编译期条件判断
      • 6.4 TMP的实际应用
    • 第七章: 模板匹配规则与SFINAE
      • 7.1 模板匹配规则
        • 7.1.1 优先调用非模板函数
        • 7.1.2 如果没有非模板函数,匹配模板实例
      • 7.2 SFINAE (Substitution Failure Is Not An Error)
        • 示例:SFINAE 规则
    • 第八章: 模板最佳实践
      • 8.1 模板的代码膨胀问题
      • 8.2 模板错误调试
    • 写在最后

C++模板进阶编程

接上篇【C++篇】引领C++模板初体验:泛型编程的力量与妙用

💬 欢迎讨论:如果你在学习过程中有任何问题或想法,欢迎在评论区留言,我们一起交流学习。你的支持是我继续创作的动力!

👍 点赞、收藏与分享:觉得这篇文章对你有帮助吗?别忘了点赞、收藏并分享给更多的小伙伴哦!你们的支持是我不断进步的动力!
🚀 分享给更多人:如果你觉得这篇文章对你有帮助,欢迎分享给更多对C++感兴趣的朋友,让我们一起进步!

前言

在C++模板编程中,基本模板的概念和用法已经能够解决大多数的编程问题,但在面对更加复杂的场景时,模板的特化、非类型模板参数以及分离编译等高级技术开始显得尤为重要。本文将详细讲解这些进阶模板知识,并结合具体示例进行剖析,帮助读者深入理解C++模板的高级用法。

第一章: 非类型模板参数

1.1 什么是非类型模板参数?

在模板编程中,除了类型参数(如 class Ttypename T)外,还可以使用非类型模板参数。非类型模板参数可以是常量,例如整数、枚举、指针等,它们在编译期间是已知的值。

1.1.1 非类型模板参数的定义

以下是一个简单的非类型模板参数的例子:

template<class T, size_t N>
class Array {
public:
    T& operator[](size_t index) {
        return _array[index];
    }

    const T& operator[](size_t index) const {
        return _array[index];
    }

    size_t size() const { return N; }

private:
    T _array[N];
};

在这个例子中,N 是一个非类型模板参数,表示数组的大小,它必须在编译时已知。

1.2 非类型模板参数的注意事项

  1. 允许的类型:非类型模板参数可以是整型、枚举、指针或者引用类型,但浮点数、类对象和字符串不允许作为非类型模板参数。
  2. 编译期确认:非类型模板参数必须在编译期确认。这意味着它的值在编译时必须是一个常量表达式。

1.3 非类型模板参数的使用场景

非类型模板参数最常用于需要对某些固定值进行编译期优化的场景。例如,在实现容器类时,可以通过非类型模板参数来指定容器的大小,从而在编译时确定内存分配的规模。

示例:静态数组的实现
template<typename T, size_t N>
class StaticArray {
public:
    T& operator[](size_t index) {
        return _array[index];
    }

    const T& operator[](size_t index) const {
        return _array[index];
    }

private:
    T _array[N];
};

int main() {
    StaticArray<int, 10> arr;  // 创建一个大小为10的静态数组
    arr[0] = 1;
    arr[1] = 2;
    std::cout << arr[0] << ", " << arr[1] << std::endl;
    return 0;
}

在这个例子中,N 是数组的大小,编译器在编译时已经知道这个值,因此它能够直接优化内存分配和数组边界检查。


第二章: 模板的特化

2.1 什么是模板特化?

模板特化是指在模板的基础上,针对某些特定的类型提供专门的实现。当模板的默认实现无法满足某些特定类型的需求时,就可以通过特化来处理。例如,针对指针类型的特殊处理。

2.1.1 模板特化的分类

模板特化分为两种:

  1. 全特化:对模板中的所有参数进行特化。
  2. 偏特化:仅对模板中的部分参数进行特化或进一步限制。

2.2 函数模板特化

示例:函数模板的特化

以下是一个函数模板特化的示例:

template<class T>
bool Less(T left, T right) {
    return left < right;
}

// 针对指针类型的特化
template<>
bool Less<Date*>(Date* left, Date* right) {
    return *left < *right;
}

int main() {
    Date d1(2022, 7, 7);
    Date d2(2022, 7, 8);

    std::cout << Less(d1, d2) << std::endl;  // 正常比较日期
    Date* p1 = &d1;
    Date* p2 = &d2;
    std::cout << Less(p1, p2) << std::endl;  // 使用特化版本,比较指针指向的内容
    return 0;
}

在这个例子中,函数 Less 针对 Date* 指针类型进行了特化,以正确处理指针类型的比较。


第三章: 类模板特化

3.1 类模板的全特化

全特化指的是对模板中的所有参数进行特化,适用于某些特定类型,完全替代原始的模板实现。

示例:全特化
template<class T1, class T2>
class Data {
public:
    Data() { std::cout << "Data<T1, T2>" << std::endl; }
};

template<>
class Data<int, char> {
public:
    Data() { std::cout << "Data<int, char>" << std::endl; }
};

int main() {
    Data<int, int> d1;   // 使用原始模板版本
    Data<int, char> d2;  // 使用全特化版本
}

在这个例子中,Data<int, char> 这个类型的对象会调用全特化的版本,输出 “Data<int, char>”。

3.2 类模板的偏特化

偏特化允许对模板的一部分参数进行特化,而不需要对全部参数进行特化。它使得模板能够更灵活地处理复杂的类型组合。

示例1:部分参数的偏特化
template<class T1, class T2>
class Data {
public:
    Data() { std::cout << "Data<T1, T2>" << std::endl; }
};

// 偏特化版本,将第二个模板参数特化为int
template<class T1>
class Data<T1, int> {
public:
    Data() { std::cout << "Data<T1, int>" << std::endl; }
};

int main() {
    Data<int, char> d1;  // 调用原始模板
    Data<int, int> d2;   // 调用偏特化版本
}

在这里,Data<int, int> 将调用偏特化版本,而 Data<int, char> 将调用原始模板版本。

示例2:指针类型的偏特化
template<class T1, class T2>
class Data {
public:
    Data() { std::cout << "Data<T1, T2>" << std::endl; }
};

// 偏特化版本,将两个参数特化为指针类型
template<class T1, class T2>
class Data<T1*, T2*> {
public:
    Data() { std::cout << "Data<T1*, T2*>" << std::endl; }
};

int main() {
    Data<int, int> d1;      // 调用原始模板
    Data<int*, int*> d2;    // 调用指针类型偏特化版本
}

在这个例子中,Data<int*, int*> 将调用偏特化的指针版本,输出 “Data<T1*, T2*>”。

3.3 类模板特化的应用示例

类模板特化在处理不同类型的对象时,能够大幅提高代码的灵活性和可读性。以下是一个具体的应用场景:

示例:对指针进行排序的类模板特化
#include <vector>
#include <algorithm>

template<class T>
struct Less {
    bool operator()(const T& x, const T& y) const {
        return x < y;
    }
};

// 针对指针类型进行特化
template<>
struct Less<Date*> {
    bool operator()(Date* x, Date* y) const {
        return *x < *y;
    }
};

int main() {
    Date d1(2022, 7, 7);
    Date d2(2022, 7, 6);
    Date d3(2022, 7, 8);

    // 排序日期对象
    std::vector<Date> v1 = {d1, d2, d3};
    std::sort(v1.begin(), v1.end(), Less<Date>());
    // 正确排序

    // 排序指针
    std::vector<Date*> v2 = {&d1, &d2, &d3};
    std::sort(v2.begin(), v2.end(), Less<Date*>());
    // 使用特化版本,按指针指向的日期排序

    return 0;
}

通过类模板特化,可以实现对指针的排序,并确保比较的是指针指向的内容而不是地址。


第四章: 模板的分离编译

4.1 什么是模板的分离编译?

分离编译指的是将程序分为多个源文件,每个源文件单独编译生成目标文件,最后将所有目标文件链接生成可执行文件。在模板编程中,分离编译有时会带来挑战,因为模板的实例化是在编译期进行的,编译器需要知道模板的定义和使用场景。

4.2 分离编译中的问题

在模板的分离编译中,模板的声明和定义分离时会产生编译或链接错误。这是因为模板的实例化是由编译器根据实际使用的类型生成的代码,如果在模板的定义和使用之间缺乏可见性,编译器无法正确地实例化模板。

示例:模板的声明和定义分离
// a.h
template<class T>
T Add(const T& left, const T& right);

// a.cpp
template<class T>
T Add(const T& left, const T& right) {
    return left + right;
}

// main.cpp
#include "a.h"

int main() {
    Add(1, 2);        // 使用模板函数
    Add(1.0, 2.0);    // 使用模板函数
    return 0;
}

在这种情况下,由于模板的定义和使用是分离的,编译器在不同编译单元中无法找到模板的定义,从而导致链接错误。

4.3 解决模板分离编译问题

为了解决模板的分离编译问题,可以采取以下几种方法:

  1. 将模板的声明和定义放在同一个头文件中
    将模板的定义和声明都放在头文件中,使得所有使用模板的编译单元都可以访问到模板的定义。

    // a.h
    template<class T>
    T Add(const T& left, const T& right) {
        return left + right;
    }
    
  2. 显式实例化模板
    通过显式实例化,将模板的具体实现放在 .cpp 文件中。这样,编译器能够在实例化时找到模板的定义。

    // a.cpp
    template T Add<int>(const int& left, const int& right);
    template T Add<double>(const double& left, const double& right);
    

这两种方法都能有效避免模板分离编译带来的问题,推荐将模板的定义和声明放在同一个文件中,通常使用 .hpp.h 文件格式。


第五章: 模板总结

模板编程在C++中是一种非常强大的工具,通过泛型编程、模板特化和非类型模板参数等技术,可以编写高效、灵活的代码。模板编程的优缺点总结如下:

优点:

  1. 代码复用:模板能够极大提高代码的复用性,减少重复代码的编写。
  2. 灵活性:可以根据不同的数据类型生成特定的代码,增强了程序的适应性。
  3. STL基础:C++的标准模板库(STL)就是基于模板技术构建的,它为容器、算法和迭代器提供了高度泛型化的接口。

缺点:

  1. 代码膨胀:模板实例化时会生成不同版本的代码,可能导致二进制文件变大。
  2. 编译时间变长:由于模板的编译期实例化,可能会导致编译时间增加。
  3. 调试困难:模板编译错误信息往往非常复杂,难以阅读和调试。

第六章: 模板元编程(Template Metaprogramming)

6.1 什么是模板元编程?

模板元编程(Template Metaprogramming,简称TMP)是一种利用C++模板机制进行编译期计算和代码生成的编程技术。它主要用于在编译时生成代码,并避免运行时的计算,从而提升程序的效率。模板元编程的核心思想是通过模板递归实现逻辑运算、数学计算等操作。

6.1.1 编译期与运行期的区别

运行期计算是在程序执行过程中进行的,例如加法运算、条件判断等。

编译期计算则是在编译阶段就确定的,模板元编程可以在程序编译过程中进行某些计算,从而减少运行期的负担。C++模板系统可以进行编译期递归和选择。


6.2 模板元编程的基础

模板元编程的基础主要是利用模板的递归和特化来进行编译期计算。一个简单的例子是使用模板递归来计算阶乘

示例:使用模板元编程计算阶乘
// 基本模板
template<int N>
struct Factorial {
    static const int value = N * Factorial<N - 1>::value;
};

// 特化版本,当N为1时终止递归
template<>
struct Factorial<1> {
    static const int value = 1;
};

int main() {
    std::cout << "Factorial of 5: " << Factorial<5>::value << std::endl;
    return 0;
}

在这个例子中,Factorial<5> 会在编译期递归展开为 5 * 4 * 3 * 2 * 1,并计算出阶乘值。在运行时打印结果,编译器已经在编译阶段完成了计算。

输出:
Factorial of 5: 120

6.3 使用模板元编程进行条件选择

模板元编程不仅可以用来进行数学运算,还可以用于条件选择(类似于 if-else 语句),从而在编译期决定代码的生成。例如,我们可以通过模板来选择某些代码块是否在编译时生成。

示例:编译期条件判断
template<bool Condition, typename TrueType, typename FalseType>
struct IfThenElse;

template<typename TrueType, typename FalseType>
struct IfThenElse<true, TrueType, FalseType> {
    typedef TrueType type;
};

template<typename TrueType, typename FalseType>
struct IfThenElse<false, TrueType, FalseType> {
    typedef FalseType type;
};

int main() {
    // 当条件为 true 时,选择 int 类型
    IfThenElse<true, int, double>::type a = 10;

    // 当条件为 false 时,选择 double 类型
    IfThenElse<false, int, double>::type b = 3.14;

    std::cout << "a: " << a << ", b: " << b << std::endl;
    return 0;
}

在这个例子中,IfThenElse 模板类模拟了条件选择,在编译时根据布尔值 Condition 选择 TrueTypeFalseType。如果条件为真,则选择 TrueType;否则,选择 FalseType


6.4 TMP的实际应用

模板元编程可以用于很多实际场景中,例如计算多项式、矩阵运算、位操作等。它的主要优势在于可以减少运行时的计算开销,将复杂的逻辑提前到编译时处理,提升程序的效率。


第七章: 模板匹配规则与SFINAE

7.1 模板匹配规则

C++编译器在调用模板时,会根据传入的模板参数进行匹配。模板匹配的规则比较复杂,涉及到多个优先级和模板特化。

7.1.1 优先调用非模板函数

在匹配时,编译器会优先选择非模板函数,如果有完全匹配的非模板函数存在,编译器会选择该函数,而不是实例化模板。

int Add(int a, int b) {
    return a + b;
}

template<typename T>
T Add(T a, T b) {
    return a + b;
}

int main() {
    int a = 1, b = 2;
    std::cout << Add(a, b) << std::endl;  // 调用非模板版本
    return 0;
}
7.1.2 如果没有非模板函数,匹配模板实例

如果没有完全匹配的非模板函数存在,编译器将生成模板实例化版本。

template<typename T>
T Add(T a, T b) {
    return a + b;
}

int main() {
    double x = 1.1, y = 2.2;
    std::cout << Add(x, y) << std::endl;  // 调用模板实例化版本
    return 0;
}

7.2 SFINAE (Substitution Failure Is Not An Error)

SFINAE 是 C++ 模板系统中的一个重要规则,全称为 “Substitution Failure Is Not An Error”(替换失败不是错误)。SFINAE 是指在模板实例化过程中,如果某些模板参数的替换失败,编译器不会直接报错,而是选择其他可行的模板。

示例:SFINAE 规则
template<typename T>
typename std::enable_if<std::is_integral<T>::value, T>::type
CheckType(T t) {
    return t * 2;
}

template<typename T>
typename std::enable_if<!std::is_integral<T>::value, T>::type
CheckType(T t) {
    return t * 0.5;
}

int main() {
    std::cout << CheckType(10) << std::endl;   // 整数类型,输出20
    std::cout << CheckType(3.14) << std::endl; // 浮点数类型,输出1.57
    return 0;
}

在这个例子中,SFINAE 机制允许我们根据类型的不同选择不同的模板版本。在 CheckType 函数模板中,当传入的参数是整数类型时,编译器选择第一个版本,而当参数是浮点数类型时,选择第二个版本。


第八章: 模板最佳实践

8.1 模板的代码膨胀问题

模板虽然提供了极大的灵活性,但它也会带来代码膨胀问题。因为模板实例化会生成多个版本的代码,所以在大规模使用模板时,可能会导致二进制文件体积增大。为了解决这个问题,可以考虑以下几种策略:

  1. 减少模板的实例化次数:通过显式实例化来控制模板的使用,避免重复生成相同功能的模板代码。
  2. 避免过度模板化:在设计模板时,尽量避免将所有逻辑都写成模板,只有在必要时才使用模板。
  3. 使用非类型模板参数:非类型模板参数可以减少模板的泛化程度,避免代码膨胀。

8.2 模板错误调试

模板编译错误通常会产生非常复杂的错误信息,难以调试。以下是一些常用的调试模板代码的方法:

  1. 分解模板代码:将复杂的模板逻辑分解为多个小的模板函数或类,逐步进行调试。
  2. 使用静态断言:在模板代码中插入 static_assert 来检查模板参数是否合法,提前发现问题。
  3. 阅读编译错误信息:虽然模板错误信息冗长,但可以从错误的上下文中找到模板参数替换的线索,从而定位问题。

写在最后

通过对C++模板进阶技术的深入讲解,我们探索了非类型模板参数、模板特化、SFINAE以及模板元编程等高级概念,这些工具不仅使我们的代码更加灵活高效,还为我们提供了在复杂场景下优化代码的思路。在实际项目中,合理利用这些模板技术可以显著提高代码复用性、减少运行时错误,并大幅提升编译期的优化效果。希望通过本篇内容的学习,你能够更好地理解并应用这些进阶技术,在未来的C++开发中游刃有余。

以上就是关于【C++篇】解密模板编程的进阶之美:参数巧思与编译的智慧的内容啦,各位大佬有什么问题欢迎在评论区指正,或者私信我也是可以的啦,您的支持是我创作的最大动力!❤️

在这里插入图片描述

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/2180808.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

YOLO11关键改进与网络结构图

目录 前言&#xff1a;一、YOLO11的优势二、YOLO11网络结构图三、C3k2作用分析四、总结 前言&#xff1a; 对于一个科研人来说&#xff0c;发表论文水平的高低和你所掌握的信息差有着极大的关系&#xff0c;所以趁着YOLO11刚刚发布&#xff0c;趁热了解&#xff0c;先人一步对…

与我免费ai书童拆解《坚持》创作历程

插科打诨的海侃胡闹&#xff0c;调侃舒展《坚持》诗创的灵魂盛宴之旅。 (笔记模板由python脚本于2024年09月30日 19:11:42创建&#xff0c;本篇笔记适合喜欢python和诗歌的coder翻阅) 【学习的细节是欢悦的历程】 Python 官网&#xff1a;https://www.python.org/ Free&#x…

如何让ollama本地模型使用code-interpreter(代码解释器)?

代码解释器通常都需要在GPU的环境下使用原生的模型通过transformer来实现&#xff0c;且本身还需要模型本身支持&#xff0c;ollama本地蒸馏过的模型占用的资源比较小&#xff0c;也方便本地使用&#xff0c;但是如果想用这些模型的代码解释器&#xff0c;即让大模型写程序并执…

小巧机身,但强劲动力实现千元级净须,未野迷你剃须刀测评

剃须刀是很多朋友每天都要用的工具&#xff0c;在选择上非常丰富&#xff0c;就便捷性和可靠性来说&#xff0c;电动剃须刀还是更方便一些。以前多数人用的都是飞利浦等传统品牌。近几年国产剃须刀也开始崛起&#xff0c;但是也存在很多令人不够满意的产品&#xff0c;比如说&a…

Redis入门第三步:Redis事务处理

欢迎继续跟随《Redis新手指南&#xff1a;从入门到精通》专栏的步伐&#xff01;在本文中&#xff0c;我们将探讨Redis的事务处理机制。了解如何使用事务来保证一系列操作的原子性和一致性&#xff0c;这对于构建可靠的应用程序至关重要 1 什么是Redis事务&#x1f340; ​ R…

高效学习工作SMART原则

S代表Specific&#xff08;明确具体的&#xff09;&#xff0c;意味着你需要清晰地定义你的目标&#xff0c;并确保它是具体而明确的。例如&#xff0c;如果你的目标是“提高销售”&#xff0c;那么这个目标就不是足够具体。更好的表述可能是&#xff1a;“在接下来的三个月内&…

【Python报错已解决】 ModuleNotFoundError: No module named ‘lime‘

&#x1f3ac; 鸽芷咕&#xff1a;个人主页 &#x1f525; 个人专栏: 《C干货基地》《粉丝福利》 ⛺️生活的理想&#xff0c;就是为了理想的生活! 专栏介绍 在软件开发和日常使用中&#xff0c;BUG是不可避免的。本专栏致力于为广大开发者和技术爱好者提供一个关于BUG解决的经…

828华为云征文 | 利用FIO工具测试Flexus云服务器X实例存储性能

目录 一、Flexus云服务器X实例概要 1.1 Flexus云服务器X实例摘要 1.2 产品特点 1.3 存储方面性能 1.4 测评服务器规格 二、FIO工具 2.1 安装部署FIO 2.2 主要性能指标概要 三、进行压测 3.1 测试全盘随机读IO延迟 3.2 测试全盘随机写IO延迟 3.3 测试随机读IOPS 3.4…

《后端程序猿 · Spring事务失效场景》

&#x1f4e2; 大家好&#xff0c;我是 【战神刘玉栋】&#xff0c;有10多年的研发经验&#xff0c;致力于前后端技术栈的知识沉淀和传播。 &#x1f497; &#x1f33b; CSDN入驻不久&#xff0c;希望大家多多支持&#xff0c;后续会继续提升文章质量&#xff0c;绝不滥竽充数…

如何使用ssm实现钢铁集团公司安全管理系统的构建与实现

TOC ssm748钢铁集团公司安全管理系统的构建与实现jsp 研究背景与现状 时代的进步使人们的生活实现了部分自动化&#xff0c;由最初的全手动办公已转向手动自动相结合的方式。比如各种办公系统、智能电子电器的出现&#xff0c;都为人们生活的享受提供帮助。采用新型的自动化…

SpringBoot教程(三十一) | SpringBoot生成Docker镜像包

SpringBoot教程&#xff08;三十&#xff09; | SpringBoot生成Docker镜像包 前提方式一&#xff1a;spring-boot-maven-plugin 方式方式二&#xff1a;Dockfile 方式&#xff08;推荐&#xff09; 前提 如果你在 Windows 上&#xff0c;确保 Docker Desktop 已经启动并正在运…

Java常用三类定时器快速入手指南

文章目录 Java常用三类定时器快速入手指南一、序言二&#xff0c;Timer相关1、概念2、Timer类3、TimerTask类4、ScheduleExecutorService接口 三&#xff0c;Scheduled相关1、配置1.1 SpringMVC配置1.2 SpringBoot配置&#xff08;1&#xff09;单线程&#xff08;2&#xff09…

python 如何引用变量

在字符串中引入变量有三种方法&#xff1a; 1、 连字符 name zhangsan print(my name is name) 结果为 my name is zhangsan 2、% 字符 name zhangsan age 25 price 4500.225 print(my name is %s%(name)) print(i am %d%(age) years old) print(my price is %f%(pric…

【数字图像处理】小白也能懂,最浅显方式手撕直方图均衡化(附python实现)

文章目录 1 概念2 原理2.1 数学原理 3 python代码实现4 测试效果5 结论 1 概念 直方图均衡化&#xff0c;同伽马变换一样&#xff0c;也是增强图像对比度的一种工具。区别在于&#xff0c;直方图均衡化是一种自适应的工具&#xff0c;即自动工具。也就是说&#xff0c;我们只需…

使用RestTemplate调用EMQX API查询MQTT客户端列表信息

项目中集成mqtt客户端查询功能&#xff0c;使用到了EMQX api-v5&#xff0c;具体步骤&#xff1a; 一、准备工作 首先在EMQX dashboard中添加API 密钥 填写密钥名称&#xff0c;点击确定&#xff0c;会生成API Key和Secret Key&#xff0c;保存起来备用。 二、配置文件 在…

SUP-NeRF-ECCV2024数据集: 单目3D对象重建的新突破

2024-09-25&#xff0c;由Bosch Research North America和Michigan State University联合发布的SUP-NeRF&#xff0c;是一个基于单目图像进行3D对象重建的新型方法。一个无缝集成姿态估计和物体重建的统一网格。 ECCV&#xff1a;欧洲计算机视觉会议的缩写&#xff0c;它是计算…

如何使用ssm实现科技银行业务管理系统+vue

TOC ssm743科技银行业务管理系统vue 第一章 绪论 1.1 研究背景 在现在社会&#xff0c;对于信息处理方面&#xff0c;是有很高的要求的&#xff0c;因为信息的产生是无时无刻的&#xff0c;并且信息产生的数量是呈几何形式的增加&#xff0c;而增加的信息如何存储以及短时间…

移除元素

移除元素 题目链接&#xff1a;移除元素 示例 1&#xff1a; 输入&#xff1a;nums [3,2,2,3], val 3 输出&#xff1a;2, nums [2,2,_,_] 解释&#xff1a;你的函数函数应该返回 k 2, 并且 nums 中的前两个元素均为 2。 你在返回的 k 个元素之外留下了什么并不重要&…

URL从输入到⻚面显示的过程(详细版)

URL从输入到⻚面显示的过程&#xff08;详细版&#xff09; 浏览器中输入网址 DNS 解析域名得到 IP 地址 DNS 解析首先会从你的浏览器的缓存中去寻找是否有这个网址对应的 IP 地址&#xff0c;如果没有就向OS系统的 DNS 缓存中寻找&#xff0c;如果没有就是路由器的 DNS 缓存&…

C++之 友元重载 以及最常用的几种友元函数

在之前的友元中就曾经讲过&#xff0c;我们为了去访问修改私有成员中的数据时&#xff0c;只能通过公有的办法去进行访问操作&#xff0c;非常的局限。所以C引用了友元函数&#xff0c;只要加上friend关键字&#xff0c;C的这个类&#xff0c;会自动把这个函数的权限拉到类内&a…