C++ 泛型编程指南02 (模板参数的类型推导)

文章目录

    • 一 深入了解C++中的函数模板类型推断
        • 什么是类型推断?
        • 使用Boost TypeIndex库进行类型推断分析
          • 示例代码
          • 关键点解析
    • 2. 理解函数模板类型推断
          • 2.1 指针或引用类型
            • 2.1.1 忽略引用
            • 2.1.2 保持const属性
            • 2.1.3 处理指针类型
          • 2.2 万能引用类型
          • 2.3 传值方式
          • 2.4 传值方式的引申—std::ref与std::cref
          • 2.5 数组作为实参
            • 2.5.1 通过值传递
            • 2.5.2 通过引用传递
          • 2.6 函数名作为实参
            • 2.6.1 通过值传递
            • 2.6.2 通过引用传递
          • 2.7 初始化列表作为实参
            • 2.7.1 包含必要的头文件
            • 2.7.2 定义一个接受初始化列表的模板函数
            • 2.7.3 使用初始化列表调用函数
          • 2.8 类型推断总结
            • 2.8.1 引用类型实参的引用属性会被忽略
            • 2.8.2 万能引用(通用引用)的推断依赖于实参是左值还是右值
            • 2.8.3 按值传递的实参,传递给形参时`const`属性不起作用
            • 2.8.4 数组或函数类型在类型推断中默认被视为指针
            • 2.8.5 初始化列表必须在函数模板的形参中明确使用`std::initializer_list<T>`
      • 三、现代C++的类型推导增强(深度解析)
        • 1. `auto` 类型推导机制
          • 基本规则
          • 推导层次分析
          • 工程实践要点
        • 2. `decltype` 类型推导系统
          • 核心行为
          • 关键应用场景
          • `decltype(auto)` 深度解析
        • 3. 结构化绑定的类型推导(C++17)
          • 基本语法形式
          • 推导规则体系
          • 实现原理
          • 工程注意事项
        • 4. 推导指南(C++17)
          • 核心概念
          • 自定义推导指南
        • 5. 类型推导的编译时验证
          • 静态断言机制
          • 概念约束(C++20)
        • 类型推导增强的底层原理
      • 总结:现代类型推导的演进趋势

一 深入了解C++中的函数模板类型推断

在现代C++编程中,类型推断是一个极其重要的概念,特别是在模板编程领域。它不仅减少了代码的冗余度,还增强了代码的灵活性和可读性。Boost库提供了一种有效的方式来查看和理解这些类型的推断结果,这对于调试和学习都非常有帮助。

什么是类型推断?

类型推断指的是编译器根据函数调用时提供的参数自动确定模板参数的类型。这种机制允许我们编写更简洁和通用的代码,而无需显式地指定所有类型。然而,有时理解编译器是如何进行类型推断的可能并不直观,尤其是在处理引用、指针和常量等复杂情况时。

使用Boost TypeIndex库进行类型推断分析

Boost库提供了一个名为boost::typeindex::type_id_with_cvr<T>()的功能,它可以返回一个表示类型的对象,该对象可以被用来获取类型的字符串表示,从而使得类型推断的结果更加清晰易读。

示例代码
#include <iostream>
#include <boost/type_index.hpp>template<typename T>
void myfunc(T&& param) 
{// 使用Boost TypeIndex库来获取并打印类型std::cout << "T is: " << boost::typeindex::type_id_with_cvr<T>().pretty_name() << std::endl;std::cout << "param is: " << boost::typeindex::type_id_with_cvr<decltype(param)>().pretty_name() << std::endl;
}int main()
{int x = 10;const int cx = x;const int& rx = x;myfunc(x);  // 应显示 "int"myfunc(cx); // 应显示 "int const"myfunc(rx); // 应显示 "int const&"myfunc(42); // 应显示 "int"
}
关键点解析
  • 安装和配置Boost:确保系统上已经安装了Boost库,并正确配置项目以包含Boost头文件路径。由于boost::typeindex是header-only库,因此无需链接任何特定的Boost库。
  • pretty_name()方法:相比标准C++的typeid().name()方法,pretty_name()提供了更加人类可读的类型名称,这在调试过程中非常有用。
  • 不同参数的影响:通过传递不同的参数(如普通变量、常量变量、常量引用),我们可以观察到编译器如何对不同类型进行推断,这有助于深入理解模板编程中的类型推断机制。

2. 理解函数模板类型推断

2.1 指针或引用类型

在C++中,当使用函数模板时,编译器通过实参来自动推导模板参数T的类型。这个过程中,对于指针或引用类型的实参,有特定的规则需要了解。

2.1.1 忽略引用

当函数模板的实参是引用类型时,编译器在进行类型推导时会忽略掉引用部分。这意味着模板参数T不会包括引用属性。

示例代码:

#include <iostream>
#include <boost/type_index.hpp>template<typename T>
void myfunc(T& param) {std::cout << "T is: " << boost::typeindex::type_id_with_cvr<T>().pretty_name() << std::endl;std::cout << "param is: " << boost::typeindex::type_id_with_cvr<decltype(param)>().pretty_name() << std::endl;
}int main() {int x = 10;int& k = x;myfunc(k);  // T 被推导为 int,而不是 int&
}

在这个示例中,尽管k是对x的引用,但是在模板推断中,T只被推导为int

2.1.2 保持const属性

当传递给函数模板的引用类型形参带有const属性的实参时,这个const属性会影响到模板参数T的推导。

示例代码:

#include <iostream>
#include <boost/type_index.hpp>template<typename T>
void myfunc(const T& param) {std::cout << "T is: " << boost::typeindex::type_id_with_cvr<T>().pretty_name() << std::endl;std::cout << "param is: " << boost::typeindex::type_id_with_cvr<decltype(param)>().pretty_name() << std::endl;
}int main() {const int j = 20;myfunc(j);  // T 被推导为 int, 形参 param 的类型为 const int&
}

在此示例中,jconst int类型,T被正确推导为int,而形参param的类型是const int&。这确保了j的常量性质不会被修改。

2.1.3 处理指针类型

对于指针类型的形参,类型推断也遵循特定的规则,尤其是在处理const修饰符时。

示例代码:

#include <iostream>
#include <boost/type_index.hpp>template <typename T>
void myfunc(T* tmprv) {std::cout << "T is: " << boost::typeindex::type_id_with_cvr<T>().pretty_name() << std::endl;std::cout << "param is: " << boost::typeindex::type_id_with_cvr<decltype(tmprv)>().pretty_name() << std::endl;
}int main() {int i = 18;const int* pi = &i;myfunc(&i);  // 查看实际执行结果:T=int, tmprv=int*myfunc(pi);  // 查看实际执行结果:T=int const, tmprv=int const*
}
  • 当调用myfunc(&i)时,&i是一个指向int的指针,因此T被推导为int,而tmprv的类型为int*
  • 当调用myfunc(pi)时,pi是一个指向const int的指针,因此T被推导为int const,而tmprv的类型为int const*
2.2 万能引用类型

万能引用(Universal References),也称为转发引用(Forwarding References),是一种特殊的引用类型,在模板函数中通过T&&声明。C++的引用折叠规则使得万能引用可以根据传入的实参类型(左值或右值)来决定其最终类型:

  • 绑定到左值:当一个左值被传递给万能引用时,万能引用将被推导为左值引用(T&)。这允许函数处理持久的对象,而不仅仅是临时值。
  • 绑定到右值:当一个右值被传递给万能引用时,万能引用将被推导为右值引用(T&&)。这使得函数能够优化资源管理,例如通过移动语义避免不必要的复制。

示例代码:

#include <iostream>
#include <boost/type_index.hpp>template<typename T>
void printType(T&& param) {std::cout << "T is: "<< boost::typeindex::type_id_with_cvr<T>().pretty_name()<< std::endl;std::cout << "param is: "<< boost::typeindex::type_id_with_cvr<decltype(param)>().pretty_name()<< std::endl;
}int main() {int x = 10;printType(x);  // 输出x为左值的类型信息printType(10); // 输出10为右值的类型信息
}
  • printType(x) 会打印出Tint&(因为x是一个左值)和param也为int&
  • printType(10) 会打印出Tint(因为10是一个右值)和paramint&&
2.3 传值方式

在C++中,当函数参数以传值方式接收时,无论原始对象是什么类型(包括指针、引用或常规变量),传递给函数的都是该对象的一个副本。这意味着在函数内部对这个副本的任何修改都不会影响到原始对象。

示例代码:

#include <iostream>
#include <boost/type_index.hpp>template <typename T>
void myfunc(T tmprv) {std::cout << "T is: "<< boost::typeindex::type_id_with_cvr<T>().pretty_name()<< std::endl;std::cout << "param is: "<< boost::typeindex::type_id_with_cvr<decltype(tmprv)>().pretty_name()<< std::endl;
}int main() {int i = 18;const int j = i;const int& k = i;myfunc(i);  // 实际执行结果:T=int, tmprv=intmyfunc(j);  // 实际执行结果:T=int, tmprv=int,const 属性没有传递,因为对方是新副本myfunc(k);  // 实际执行结果:T=int, tmprv=int,const 属性和引用都没有传递,因为对方是新副本
}

结论:

  • 忽略引用性:若实参是引用类型,则引用部分会被忽略,T不会被推导为引用类型。
  • 忽略顶层const:若实参是const类型,该const属性在类型推导时会被忽略,因为传递的是新副本。
2.4 传值方式的引申—std::ref与std::cref

为了减少不必要的数据复制,C++11提供了std::refstd::cref,它们定义在<functional>头文件中。这两个工具允许以引用的形式传递参数,而不是复制对象:

  • std::ref用于创建一个对象的引用。
  • std::cref用于创建一个对象的常量引用。

示例代码:

#include <iostream>
#include <functional>
#include <boost/type_index.hpp>template<typename T>
void myfunc(T tmprv) {std::cout << "T is: "<< boost::typeindex::type_id_with_cvr<T>().pretty_name()<< std::endl;std::cout << "param is: "<< boost::typeindex::type_id_with_cvr<decltype(tmprv)>().pretty_name()<< std::endl;
}int main() {int x = 10;const int y = 20;// 传值方式myfunc(x);myfunc(y);// 使用 std::ref 和 std::crefmyfunc(std::ref(x));myfunc(std::cref(y));return 0;
}
  • myfunc(x)myfunc(y)通过值传递调用,T被推导为intconst int
  • myfunc(std::ref(x))myfunc(std::cref(y))通过引用传递调用,T被推导为int&const int&
2.5 数组作为实参

当数组被用作函数模板的实参时,其处理方式依赖于参数传递的方式:是通过值还是通过引用。

2.5.1 通过值传递

在C++中,当数组作为函数参数通过值传递时,它不会将整个数组的副本传递给函数,而是只传递一个指向数组首元素的指针,这种现象称为“数组退化”。

示例代码:

#include <iostream>
#include <boost/type_index.hpp>template <typename T>
void myfunc(T tmprv) {std::cout << "T is: "<< boost::typeindex::type_id_with_cvr<T>().pretty_name()<< std::endl;std::cout << "param is: "<< boost::typeindex::type_id_with_cvr<decltype(tmprv)>().pretty_name()<< std::endl;
}int main() {const char mystr[] = "I Love China!";myfunc(mystr); // 实际执行结果:T=const char*, tmprv=const char*
}

这里,mystr被退化为const char*,因此Tconst char*

2.5.2 通过引用传递

修改函数模板使其通过引用接收参数可以保留数组的完整性,避免退化为指针。

示例代码:

#include <iostream>
#include <boost/type_index.hpp>template <typename T>
void myfunc(T& tmprv) {std::cout << "T is: "<< boost::typeindex::type_id_with_cvr<T>().pretty_name()<< std::endl;std::cout << "param is: "<< boost::typeindex::type_id_with_cvr<decltype(tmprv)>().pretty_name()<< std::endl;
}int main() {const char mystr[] = "I Love China!";myfunc(mystr); // 实际执行结果:T=const char [14], tmprv=const char (&)[14]
}

在这种情况下,数组不会退化,T被推导为具体的数组类型const char [14],而tmprv是该数组的引用。

2.6 函数名作为实参

在C++中,函数名可以用作函数模板的实参,在编写需要回调函数的代码或实现高阶函数时,经常需要将函数名作为参数传递给其他函数。使用模板可以使这类函数更加通用和灵活。

2.6.1 通过值传递

当函数名作为实参传递时,默认被视为指向该函数的指针。

示例代码:

#include <iostream>
#include <boost/type_index.hpp>void testFunc() {}template <typename T>
void myfunc(T tmprv) {std::cout << "T is: "<< boost::typeindex::type_id_with_cvr<T>().pretty_name()<< std::endl;std::cout << "param is: "<< boost::typeindex::type_id_with_cvr<decltype(tmprv)>().pretty_name()<< std::endl;
}int main() {myfunc(testFunc); // 实际执行结果:T=void (*)(void), tmprv=void (*)(void)
}

这里,testFunc被视为void (*)(void)类型,即指向无参、无返回值函数的指针。

2.6.2 通过引用传递

通过修改模板参数为引用类型,可以获取到函数的引用,而非指针。

示例代码:

#include <iostream>
#include <boost/type_index.hpp>void testFunc() {}template <typename T>
void myfunc(T& tmprv) {std::cout << "T is: "<< boost::typeindex::type_id_with_cvr<T>().pretty_name()<< std::endl;std::cout << "param is: "<< boost::typeindex::type_id_with_cvr<decltype(tmprv)>().pretty_name()<< std::endl;
}int main() {myfunc(testFunc); // 实际执行结果:T=void (&)(void), tmprv=void (&)(void)
}

在这种情况下,Ttmprv被推导为函数的引用类型void (&)(void),这显示了C++对函数的引用支持。


2.7 初始化列表作为实参

在C++中,初始化列表(std::initializer_list)提供了一种方便的方法来处理未知数量的同类型参数。这在模板编程中尤其有用,因为它允许函数接受任意数量的同类型参数,而无需预先定义参数的数量。

2.7.1 包含必要的头文件

要使用std::initializer_list,首先需要包含相应的头文件。通常,这会是<initializer_list>,它定义了std::initializer_list类。此外,为了进行输出等操作,可以包含<iostream>头文件:

#include <initializer_list>
#include <iostream>
#include <boost/type_index.hpp> // 需要包含 Boost TypeIndex 头文件
2.7.2 定义一个接受初始化列表的模板函数

你可以定义一个模板函数,该函数接受一个类型为std::initializer_list<T>的参数。这允许你传递一个由花括号 {} 包围的元素列表给函数,如下所示:

template <typename T>
void myfunc(std::initializer_list<T> tmprv) {for (const auto& item : tmprv) {std::cout << item << " ";}std::cout << std::endl;// 打印类型信息std::cout << "T is: "<< boost::typeindex::type_id_with_cvr<T>().pretty_name()<< std::endl;std::cout << "param is: "<< boost::typeindex::type_id_with_cvr<decltype(tmprv)>().pretty_name()<< std::endl;
}

在这个函数中,遍历初始化列表中的每个元素,并将其打印出来。通过打印出每个元素的类型和参数的类型,可以更深入地理解类型推断和模板的行为。使用基于范围的for循环使代码简洁且易于理解。

2.7.3 使用初始化列表调用函数

在主函数中,你可以通过以下方式调用myfunc,传入一个初始化列表:

int main() {myfunc({1, 2, 3, 4, 5}); // 调用模板函数并传入一个整数列表myfunc({"apple", "banana", "cherry"}); // 调用相同的模板函数并传入一个字符串列表
}

这样的调用方式表明,myfunc能够接受任何类型的元素列表,只要这些元素类型相同。每次调用时,模板参数T被推导为列表中元素的类型,无论是intstd::string还是其他任何类型。

2.8 类型推断总结

在C++模板编程中,类型推断遵循一些特定的规则,这些规则决定了如何根据实参推导模板参数的类型。以下是关于类型推断的一些关键点总结:

2.8.1 引用类型实参的引用属性会被忽略

在类型推断过程中,如果实参是引用类型,其引用属性会被忽略。这意味着不论实参是左值引用还是右值引用,都会被视为其底层类型进行推断。

示例:

template<typename T>
void myfunc(T& param) {std::cout << "T is: " << boost::typeindex::type_id_with_cvr<T>().pretty_name() << std::endl;
}int x = 10;
int& k = x;
myfunc(k);  // T 被推导为 int,而不是 int&
2.8.2 万能引用(通用引用)的推断依赖于实参是左值还是右值

当模板参数按值传递时,实参的const属性不影响推断结果,因此const修饰符会被忽略。然而,如果传递的是指向const的指针或引用,其const属性仍然保留。

示例:

template<typename T>
void printType(T&& param) {std::cout << "T is: "<< boost::typeindex::type_id_with_cvr<T>().pretty_name()<< std::endl;std::cout << "param is: "<< boost::typeindex::type_id_with_cvr<decltype(param)>().pretty_name()<< std::endl;
}int main() {int x = 10;printType(x);  // 输出x为左值的类型信息printType(10); // 输出10为右值的类型信息
}
  • printType(x) 会打印出Tint&(因为x是一个左值)和param也为int&
  • printType(10) 会打印出Tint(因为10是一个右值)和paramint&&
2.8.3 按值传递的实参,传递给形参时const属性不起作用

当模板参数按值传递时,实参的const属性不影响推断结果,因此const修饰符会被忽略。然而,如果传递的是指向const的指针或引用,其const属性仍然保留。

示例:

template <typename T>
void myfunc(T tmprv) {std::cout << "T is: "<< boost::typeindex::type_id_with_cvr<T>().pretty_name()<< std::endl;std::cout << "param is: "<< boost::typeindex::type_id_with_cvr<decltype(tmprv)>().pretty_name()<< std::endl;
}int main() {const int j = 20;myfunc(j);  // 实际执行结果:T=int, tmprv=int,const 属性没有传递,因为对方是新副本
}
2.8.4 数组或函数类型在类型推断中默认被视为指针

在类型推断中,数组或函数名将退化为相应的指针类型,除非模板形参明确声明为引用类型,这时候不会发生退化。

示例:

template <typename T>
void myfunc(T tmprv) {std::cout << "T is: "<< boost::typeindex::type_id_with_cvr<T>().pretty_name()<< std::endl;std::cout << "param is: "<< boost::typeindex::type_id_with_cvr<decltype(tmprv)>().pretty_name()<< std::endl;
}int main() {const char mystr[] = "I Love China!";myfunc(mystr); // 实际执行结果:T=const char*, tmprv=const char*
}
2.8.5 初始化列表必须在函数模板的形参中明确使用std::initializer_list<T>

std::initializer_list类型无法自动从花括号初始化列表推断得出,必须在函数模板的形参中显式声明为std::initializer_list<T>类型。

示例:

template <typename T>
void myfunc(std::initializer_list<T> tmprv) {for (const auto& item : tmprv) {std::cout << item << " ";}std::cout << std::endl;// 打印类型信息std::cout << "T is: "<< boost::typeindex::type_id_with_cvr<T>().pretty_name()<< std::endl;std::cout << "param is: "<< boost::typeindex::type_id_with_cvr<decltype(tmprv)>().pretty_name()<< std::endl;
}int main() {myfunc({1, 2, 3, 4, 5}); // 调用模板函数并传入一个整数列表myfunc({"apple", "banana", "cherry"}); // 调用相同的模板函数并传入一个字符串列表
}

三、现代C++的类型推导增强(深度解析)

现代C++(C++11及后续标准)对类型推导机制进行了重大增强,极大提升了代码的表达能力和安全性。以下从核心机制、典型应用和底层原理三个维度深入解析这些增强特性。


1. auto 类型推导机制
基本规则
  • 遵循模板类型推导规则:与模板函数参数推导机制一致
  • 例外处理:对初始化列表的特殊处理
auto x = 5;         // int
auto y = {1, 2, 3}; // std::initializer_list<int>(C++11特有规则)
推导层次分析
  1. 基本推导

    const int ci = 42;
    auto a = ci;      // int(丢弃顶层const)
    auto& b = ci;     // const int&(保留底层const)
    
  2. 引用折叠应用

    int i = 10;
    auto&& r1 = i;    // int&(左值推导)
    auto&& r2 = 42;   // int&&(右值推导)
    
  3. 指针与数组处理

    const char name[] = "C++";
    auto arr1 = name;  // const char*(数组退化为指针)
    auto& arr2 = name; // const char(&)[4](保留数组类型)
    
工程实践要点
  • 性能优化
    std::vector<std::string> heavyData;
    for (const auto& elem : heavyData) { ... }  // 避免拷贝
    
  • 类型精确控制
    auto ptr = static_cast<float*>(malloc(100 * sizeof(float)));  // 明确指针类型
    

2. decltype 类型推导系统
核心行为
  • 精确保留表达式类型(包括CV限定符和引用属性)
int x = 0;
const int& crx = x;
decltype(x) a;     // int
decltype(crx) b;   // const int&
decltype((x)) c;   // int&(注意括号的影响)
关键应用场景
  1. 返回值类型推导

    template<typename T1, typename T2>
    auto add(T1 a, T2 b) -> decltype(a + b) {return a + b;
    }
    
  2. 类型关系维护

    template<typename Container>
    auto getElement(Container& c, size_t index) -> decltype(c[index]) {return c[index];  // 完美保留返回类型(可能为引用)
    }
    
  3. 元编程支持

    template<typename T>
    using RemoveReference = typename std::remove_reference<decltype(std::declval<T>())>::type;
    
decltype(auto) 深度解析
  • 设计目标:在单表达式场景中完美转发类型信息
  • 典型用例
    template<typename F, typename... Args>
    decltype(auto) callFunc(F&& f, Args&&... args) {return std::forward<F>(f)(std::forward<Args>(args)...);
    }
    
  • auto对比
    const int x = 42;
    auto a = x;            // int
    decltype(auto) b = x;  // const int
    auto& c = x;           // const int&
    decltype(auto) d = (x);// const int&(注意括号的语义变化)
    

3. 结构化绑定的类型推导(C++17)
基本语法形式
auto [var1, var2, ...] = expression;
auto& [var1, var2, ...] = expression;
推导规则体系
  1. 绑定到非嵌套类型

    std::pair<int, std::string> p{42, "answer"};
    auto& [num, text] = p;  // num: int&, text: std::string&
    
  2. 绑定到结构体成员

    struct Point { double x, y; };
    Point pt{1.0, 2.0};
    const auto [a, b] = pt;  // a: const double, b: const double
    
  3. 绑定到数组元素

    int arr[] = {1, 2, 3};
    auto [x, y, z] = arr;     // x,y,z: int
    auto& [rx, ry, rz] = arr; // rx,ry,rz: int&
    
实现原理
  • 隐藏代理对象:编译器生成匿名结构体保存引用
  • 访问器方法:实际通过get<N>系列函数实现访问
工程注意事项
  • 生命周期管理
    auto getData() -> std::tuple<std::vector<int>, std::string>;auto [vec, str] = getData();  // vec和str是拷贝的独立对象
    auto& [rvec, rstr] = getData(); // 危险!临时对象立即销毁
    

4. 推导指南(C++17)
核心概念
  • 用户自定义推导规则:指导类模板参数推导
  • 标准库应用示例
    std::vector v{1, 2, 3};  // 推导为vector<int>
    std::mutex mtx;
    std::lock_guard lck(mtx); // 推导为lock_guard<mutex>
    
自定义推导指南
template<typename T>
struct CustomWrapper {template<typename U>CustomWrapper(U&& u) : t(std::forward<U>(u)) {}T t;
};// 用户定义的推导指南
template<typename U>
CustomWrapper(U) -> CustomWrapper<std::decay_t<U>>;

5. 类型推导的编译时验证
静态断言机制
template<typename T>
void process(T&& param) {static_assert(std::is_integral_v<std::decay_t<T>>,"Requires integral type");
}
概念约束(C++20)
template<std::integral T>
auto safe_divide(T a, T b) -> std::optional<T> {if (b == 0) return std::nullopt;return a / b;
}

类型推导增强的底层原理
  1. 编译器前端处理

    • 词法分析阶段识别类型推导关键字
    • 语法分析阶段构建推导上下文
  2. 类型系统交互

    • 结合重载决议规则(Overload Resolution)
    • 引用折叠(Reference Collapsing)的实现
  3. 模板实例化过程

    • 两阶段查找(Two-phase lookup)的影响
    • SFINAE(Substitution Failure Is Not An Error)机制

总结:现代类型推导的演进趋势

特性C++11C++14C++17C++20
auto基本推导规则函数返回类型推导非类型模板参数推导概念约束
decltype基本功能decltype(auto)结构化绑定中的推导更精细的类型特征检查
推导指南N/AN/A类模板参数推导增强的CTAD规则
模式匹配基础模板元编程改进的SFINAE结构化绑定模式匹配提案推进

通过掌握这些增强特性,开发者可以:

  1. 编写更简洁、类型安全的泛型代码
  2. 实现高效的资源管理(避免不必要的拷贝)
  3. 构建更灵活的接口设计
  4. 提升模板元编程的表达能力
  5. 更好地与现代C++标准库协同工作

建议通过编译器资源管理器(Compiler Explorer)实时观察不同类型推导的结果,结合标准文档深入理解各个特性的设计哲学和实现细节。

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

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

NLP深度学习 DAY4:Word2Vec详解:两种模式(CBOW与Skip-gram)

用稀疏向量表示文本&#xff0c;即所谓的词袋模型在 NLP 有着悠久的历史。正如上文中介绍的&#xff0c;早在 2001年就开始使用密集向量表示词或词嵌入。Mikolov等人在2013年提出的创新技术是通过去除隐藏层&#xff0c;逼近目标&#xff0c;进而使这些单词嵌入的训练更加高效。…

HarmonyOS简介:应用开发的机遇、挑战和趋势

问题 更多的智能设备并没有带来更好的全场景体验 连接步骤复杂数据难以互通生态无法共享能力难以协同 主要挑战 针对不同设备上的不同操作系统&#xff0c;重复开发&#xff0c;维护多套版本 多种语言栈&#xff0c;对人员技能要求高 多种开发框架&#xff0c;不同的编程…

Windows11 不依赖docker搭建 deepseek-R1 1.5B版本(附 Open WebUi搭建方式)

零、前言 过年这几天发现 DeepSeek 非常火&#xff0c;试用了一下发现确实不错。与豆包、kimi、perplexity 这些相比完全不是一个次元的存在&#xff0c;特别是用ta写文章的时候体验非常好。所以试着自己搭一个环境。 一、安装 Ollama和DeepSeek-R1 我的安装方式很简单&#xf…

解决whisper 本地运行时GPU 利用率不高的问题

我在windows 环境下本地运行whisper 模型&#xff0c;使用的是nivdia RTX4070 显卡&#xff0c;结果发现GPU 的利用率只有2% 。使用 import torch print(torch.cuda.is_available()) 返回TRUE。表示我的cuda 是可用的。 最后在github 的下列网页上找到了问题 极低的 GPU 利…

springCload快速入门

原作者&#xff1a;3. SpringCloud - 快速通关 前置知识&#xff1a; Java17及以上、MavenSpringBoot、SpringMVC、MyBatisLinux、Docker 1. 分布式基础 1.1. 微服务 微服务架构风格&#xff0c;就像是把一个单独的应用程序开发为一套小服务&#xff0c;每个小服务运行在自…

Gradle配置指南:深入解析settings.gradle.kts(Kotlin DSL版)

文章目录 Gradle配置指南&#xff1a;深入解析settings.gradle.kts&#xff08;Kotlin DSL版&#xff09;settings.gradle.kts 基础配置选项单项目配置多项目配置 高级配置选项插件管理&#xff08;Plugin Management&#xff09;基础配置模板案例&#xff1a;Android项目标准配…

C++ Primer 标准库类型string

欢迎阅读我的 【CPrimer】专栏 专栏简介&#xff1a;本专栏主要面向C初学者&#xff0c;解释C的一些基本概念和基础语言特性&#xff0c;涉及C标准库的用法&#xff0c;面向对象特性&#xff0c;泛型特性高级用法。通过使用标准库中定义的抽象设施&#xff0c;使你更加适应高级…

[EAI-028] Diffusion-VLA,能够进行多模态推理和机器人动作预测的VLA模型

Paper Card 论文标题&#xff1a;Diffusion-VLA: Scaling Robot Foundation Models via Unified Diffusion and Autoregression 论文作者&#xff1a;Junjie Wen, Minjie Zhu, Yichen Zhu, Zhibin Tang, Jinming Li, Zhongyi Zhou, Chengmeng Li, Xiaoyu Liu, Yaxin Peng, Chao…

使用MATLAB进行雷达数据采集可视化

本文使用轮趣科技N10雷达&#xff0c;需要源码可在后台私信或者资源自取 1. 项目概述 本项目旨在通过 MATLAB 读取 N10 激光雷达 的数据&#xff0c;并进行 实时 3D 点云可视化。数据通过 串口 传输&#xff0c;并经过解析后转换为 三维坐标点&#xff0c;最终使用 pcplayer 进…

UE求职Demo开发日志#19 给物品找图标,实现装备增加属性,背包栏UI显示装备

1 将用到的图标找好&#xff0c;放一起 DataTable里对应好图标 测试一下能正确获取&#xff1a; 2 装备增强属性思路 给FMyItemInfo添加一个枚举变量记录类型&#xff08;物品&#xff0c;道具&#xff0c;装备&#xff0c;饰品&#xff0c;武器&#xff09;--> 扩展DataT…

Docker 部署 Starrocks 教程

Docker 部署 Starrocks 教程 StarRocks 是一款高性能的分布式分析型数据库&#xff0c;主要用于 OLAP&#xff08;在线分析处理&#xff09;场景。它最初是由百度的开源团队开发的&#xff0c;旨在为大数据分析提供一个高效、低延迟的解决方案。StarRocks 支持实时数据分析&am…

(9) 上:学习与验证 linux 里的 epoll 对象里的 EPOLLIN、 EPOLLHUP 与 EPOLLRDHUP 的不同

&#xff08;1&#xff09;经过之前的学习。俺认为结论是这样的&#xff0c;因为三次握手到四次挥手&#xff0c;到 RST 报文&#xff0c;都是 tcp 连接上收到了报文&#xff0c;这都属于读事件。所以&#xff1a; EPOLLIN : 包含了读事件&#xff0c; FIN 报文的正常四次挥手、…

python学opencv|读取图像(五十二)使用cv.matchTemplate()函数实现最佳图像匹配

【1】引言 前序学习了图像的常规读取和基本按位操作技巧&#xff0c;相关文章包括且不限于&#xff1a; python学opencv|读取图像-CSDN博客 python学opencv|读取图像&#xff08;四十九&#xff09;原理探究&#xff1a;使用cv2.bitwise()系列函数实现图像按位运算-CSDN博客…

数据分析系列--⑦RapidMiner模型评价(基于泰坦尼克号案例含数据集)

一、前提 二、模型评估 1.改造⑥ 2.Cross Validation算子说明 2.1Cross Validation 的作用 2.1.1 模型评估 2.1.2 减少过拟合 2.1.3 数据利用 2.2 Cross Validation 的工作原理 2.2.1 数据分割 2.2.2 迭代训练与测试 ​​​​​​​ 2.2.3 结果汇总 ​​​​​​​ …

DeepSeek r1本地安装全指南

环境基本要求 硬件配置 需要本地跑模型&#xff0c;兼顾质量、性能、速度以及满足日常开发需要&#xff0c;我们需要准备以下硬件&#xff1a; CPU&#xff1a;I9内存&#xff1a;128GB硬盘&#xff1a;3-4TB 最新SSD&#xff0c;C盘确保有400GB&#xff0c;其它都可划成D盘…

AI开发学习之——PyTorch框架

PyTorch 简介 PyTorch &#xff08;Python torch&#xff09;是由 Facebook AI 研究团队开发的开源机器学习库&#xff0c;广泛应用于深度学习研究和生产。它以动态计算图和易用性著称&#xff0c;支持 GPU 加速计算&#xff0c;并提供丰富的工具和模块。 PyTorch的主要特点 …

纯后训练做出benchmark超过DeepseekV3的模型?

论文地址 https://arxiv.org/pdf/2411.15124 模型是AI2的&#xff0c;他们家也是玩开源的 先看benchmark&#xff0c;几乎是纯用llama3 405B后训练去硬刚出一个gpt4o等级的LLamA405 我们先看之前的机遇Lllama3.1 405B进行全量微调的模型 Hermes 3&#xff0c;看着还没缘模型…

像接口契约文档 这种工件,在需求 分析 设计 工作流里面 属于哪一个工作流

οゞ浪漫心情ゞο(20***328) 2016/2/18 10:26:47 请教一下&#xff0c;像接口契约文档 这种工件&#xff0c;在需求 分析 设计 工作流里面 属于哪一个工作流&#xff1f; 潘加宇(35***47) 17:17:28 你这相当于问用例图、序列图属于哪个工作流&#xff0c;看内容。 如果你的&quo…

代码随想录刷题笔记

数组 二分查找 ● 704.二分查找 tips&#xff1a;两种方法&#xff0c;左闭右开和左闭右闭&#xff0c;要注意区间不变性&#xff0c;在判断mid的值时要看mid当前是否使用过 ● 35.搜索插入位置 ● 34.在排序数组中查找元素的第一个和最后一个位置 tips&#xff1a;寻找左右边…

PyTorch框架——基于深度学习YOLOv8神经网络学生课堂行为检测识别系统

基于YOLOv8深度学习的学生课堂行为检测识别系统&#xff0c;其能识别三种学生课堂行为&#xff1a;names: [举手, 读书, 写字] 具体图片见如下&#xff1a; 第一步&#xff1a;YOLOv8介绍 YOLOv8 是 ultralytics 公司在 2023 年 1月 10 号开源的 YOLOv5 的下一个重大更新版本…