当前位置: 首页 > news >正文

C++学习:六个月从基础到就业——C++11/14:decltype关键字

C++学习:六个月从基础到就业——C++11/14:decltype关键字

本文是我C++学习之旅系列的第四十二篇技术文章,也是第三阶段"现代C++特性"的第四篇,主要介绍C++11/14中的decltype关键字。查看完整系列目录了解更多内容。

引言

在现代C++编程中,类型推导是提高代码灵活性和可读性的重要机制。上一篇文章中,我们详细介绍了auto关键字,它能根据初始化表达式自动推导变量的类型。而本文将介绍的decltype关键字则提供了一种更精确的类型推导方式,它可以在不实际计算表达式的情况下获取表达式的类型。

C++11引入decltype的主要目的是支持泛型编程中的后置返回类型,以及处理依赖于模板参数的复杂类型。C++14进一步扩展了其功能,引入了decltype(auto),简化了模板编程和完美转发。本文将详细讲解decltype的工作原理、用法以及实际应用场景,帮助你掌握这一强大的现代C++特性。

目录

  • decltype基础
    • decltype的概念与语法
    • decltype与auto的区别
  • decltype的类型推导规则
    • 标识符和成员访问表达式
    • 函数调用表达式
    • 带括号的表达式和左值表达式
    • 结合cv限定符和引用
  • decltype实际应用
    • 模板函数中的返回类型推导
    • decltype与auto结合使用
    • 带有decltype的统一初始化语法
    • 元函数中的类型计算
  • C++14中的decltype(auto)
    • 函数返回类型的使用
  • 实际应用示例
    • 示例1:通用容器访问函数
    • 示例2:泛型算法
    • 示例3:高级类型特性
  • 最佳实践与注意事项
    • 何时使用decltype
    • 避免常见陷阱
    • 风格建议
  • 总结

decltype基础

decltype的概念与语法

decltype是C++11引入的一个关键字,用于获取表达式的类型,而不实际计算表达式的值。其基本语法形式为:

decltype(expression)

decltype操作符会分析expression并返回其类型,这个过程发生在编译时,不会影响程序的运行时行为。

下面是一些基本示例:

#include <iostream>
#include <typeinfo>
#include <vector>int main() {// 基本类型int i = 42;decltype(i) j = i * 2;  // j的类型是int// 复杂表达式double d = 3.14;decltype(i + d) result = i + d;  // result的类型是double// 函数返回值类型auto func = []() -> double { return 1.0; };decltype(func()) value = 2.5;  // value的类型是double// 容器类型std::vector<int> vec = {1, 2, 3};decltype(vec) anotherVec;  // anotherVec的类型是std::vector<int>decltype(vec[0]) element = 5;  // element的类型是int&std::cout << "j = " << j << ", type: " << typeid(j).name() << std::endl;std::cout << "result = " << result << ", type: " << typeid(result).name() << std::endl;std::cout << "value = " << value << ", type: " << typeid(value).name() << std::endl;return 0;
}

decltype与auto的区别

虽然decltypeauto都涉及类型推导,但它们的推导规则和使用场景有明显区别:

  1. 推导规则

    • auto根据初始化表达式的类型进行推导,会去除引用和顶层const限定符
    • decltype根据表达式的确切类型进行推导,保留引用和const限定符
  2. 使用场景

    • auto主要用于变量声明时自动推导类型
    • decltype主要用于获取表达式类型,特别适用于模板编程和后置返回类型

下面的例子展示了两者的区别:

#include <iostream>
#include <type_traits>int main() {// auto与decltype对引用的处理不同int x = 10;int& rx = x;auto a = rx;        // a的类型是int(auto丢弃了引用)decltype(rx) b = x;  // b的类型是int&(decltype保留了引用)// auto与decltype对const的处理不同const int cx = 20;auto c = cx;           // c的类型是int(auto丢弃了顶层const)decltype(cx) d = 20;   // d的类型是const int(decltype保留了const)// 验证类型std::cout << "a is reference? " << std::boolalpha << std::is_reference<decltype(a)>::value << std::endl;  // falsestd::cout << "b is reference? " << std::boolalpha << std::is_reference<decltype(b)>::value << std::endl;  // truestd::cout << "c is const? " << std::boolalpha << std::is_const<std::remove_reference_t<decltype(c)>>::value << std::endl;  // falsestd::cout << "d is const? " << std::boolalpha << std::is_const<std::remove_reference_t<decltype(d)>>::value << std::endl;  // truereturn 0;
}

decltype的类型推导规则

decltype的类型推导规则比auto更复杂,主要有以下几点:

1. 标识符和成员访问表达式

decltype的参数是一个标识符(变量名)或者类成员访问表达式(如obj.member)时,decltype返回该标识符或成员的声明类型:

#include <iostream>
#include <type_traits>struct S {int x;double y;
};int main() {int i = 42;const int ci = i;int& ri = i;const int& cri = i;// 标识符的decltypedecltype(i) di = i;     // di的类型是intdecltype(ci) dci = ci;  // dci的类型是const intdecltype(ri) dri = i;   // dri的类型是int&decltype(cri) dcri = i; // dcri的类型是const int&// 成员访问表达式S s = {42, 3.14};decltype(s.x) dx = s.x;  // dx的类型是intdecltype(s.y) dy = s.y;  // dy的类型是doublestd::cout << "decltype(i) is int? " << std::boolalpha << std::is_same<decltype(i), int>::value << std::endl;std::cout << "decltype(ci) is const int? " << std::boolalpha << std::is_same<decltype(ci), const int>::value << std::endl;std::cout << "decltype(ri) is int&? " << std::boolalpha << std::is_same<decltype(ri), int&>::value << std::endl;return 0;
}

2. 函数调用表达式

decltype的参数是函数调用表达式时,decltype返回函数的返回类型:

#include <vector>
#include <type_traits>
#include <iostream>// 返回值为int的函数
int getInt() {return 42;
}// 返回值为引用的函数
int& getIntRef() {static int x = 42;return x;
}int main() {// 函数返回值的decltypedecltype(getInt()) i = 10;      // i的类型是intdecltype(getIntRef()) ir = i;   // ir的类型是int&// 标准库函数返回值std::vector<int> vec = {1, 2, 3};decltype(vec.size()) size = 10;    // size的类型是std::vector<int>::size_typedecltype(vec[0]) elem = vec[0];    // elem的类型是int&std::cout << "decltype(getInt()) is int? " << std::boolalpha << std::is_same<decltype(getInt()), int>::value << std::endl;std::cout << "decltype(getIntRef()) is int&? " << std::boolalpha << std::is_same<decltype(getIntRef()), int&>::value << std::endl;return 0;
}

注意:对于函数调用表达式,decltype只分析函数返回类型,不实际调用函数。

3. 带括号的表达式和左值表达式

这是decltype最复杂也最容易混淆的部分:

  • 如果表达式是一个被额外括号包围的标识符,或者是一个类左值表达式(即可以出现在赋值号左侧的表达式),那么decltype将返回该表达式的引用类型。

这条规则导致了下面这种奇怪的行为:

#include <iostream>
#include <type_traits>int main() {int x = 10;decltype(x) a = x;      // a的类型是intdecltype((x)) b = x;    // b的类型是int&,因为(x)是一个左值表达式std::cout << "decltype(x) is int? " << std::boolalpha << std::is_same<decltype(x), int>::value << std::endl;  // truestd::cout << "decltype((x)) is int&? " << std::boolalpha << std::is_same<decltype((x)), int&>::value << std::endl;  // true// 左值表达式int arr[5] = {1, 2, 3, 4, 5};decltype(arr[3]) c = arr[0];  // c的类型是int&,因为arr[3]是左值表达式// 复杂表达式int i = 10, j = 20;decltype(i = j) d = i;  // d的类型是int&,因为赋值表达式返回左值引用return 0;
}

4. 结合cv限定符和引用

decltype精确保留表达式类型的cv限定符(const和volatile)以及引用特性:

#include <iostream>
#include <type_traits>int main() {int x = 10;const int cx = 20;const int& crx = x;// 基本类型与cv限定符decltype(x) dx = x;       // dx的类型是intdecltype(cx) dcx = 20;    // dcx的类型是const intdecltype(crx) dcrx = x;   // dcrx的类型是const int&// 指针与cv限定符const int* pcx = &cx;int* const cpy = &x;decltype(pcx) dpcx = &cx; // dpcx的类型是const int*decltype(cpy) dcpy = &x;  // dcpy的类型是int* const// 验证类型std::cout << "decltype(cx) is const int? " << std::boolalpha << std::is_same<decltype(cx), const int>::value << std::endl;std::cout << "decltype(crx) is const int&? " << std::boolalpha << std::is_same<decltype(crx), const int&>::value << std::endl;std::cout << "decltype(pcx) is const int*? " << std::boolalpha << std::is_same<decltype(pcx), const int*>::value << std::endl;std::cout << "decltype(cpy) is int* const? " << std::boolalpha << std::is_same<decltype(cpy), int* const>::value << std::endl;return 0;
}

这些规则使得decltype在泛型编程中特别有用,因为它能精确地捕获表达式类型的所有细节。

decltype实际应用

模板函数中的返回类型推导

decltype最初的主要用途是推导模板函数的返回类型,特别是返回类型依赖于参数类型的情况:

#include <iostream>
#include <vector>// C++11后置返回类型语法
template<typename Container>
auto getFirstElement(Container& c) -> decltype(c[0]) {return c[0];
}int main() {std::vector<int> intVec = {1, 2, 3};std::vector<std::string> strVec = {"hello", "world"};// getFirstElement返回引用类型auto& intElem = getFirstElement(intVec);auto& strElem = getFirstElement(strVec);// 修改引用会影响原始容器intElem = 100;strElem = "modified";std::cout << "intVec[0] = " << intVec[0] << std::endl;  // 100std::cout << "strVec[0] = " << strVec[0] << std::endl;  // modifiedreturn 0;
}

在上面的例子中,decltype(c[0])会准确推导出表达式c[0]的类型,对于std::vector<int>int&,对于std::vector<std::string>std::string&

decltype与auto结合使用

在C++11中,常常需要将decltypeauto结合使用,例如为模板函数推导精确的返回类型:

#include <iostream>// 普通版本函数,无法推导类型
template<typename T, typename U>
auto multiply(T t, U u) -> decltype(t * u) {return t * u;
}// 数组版本函数,返回元素引用
template<typename T, size_t N>
auto getElement(T (&arr)[N], size_t i) -> decltype(arr[i]) {return arr[i];
}int main() {// 使用multiply函数auto result1 = multiply(5, 3.14);      // 结果是doubleauto result2 = multiply(5.0, 3);       // 结果是doublestd::cout << "5 * 3.14 = " << result1 << std::endl;std::cout << "5.0 * 3 = " << result2 << std::endl;// 使用getElement函数int arr[] = {1, 2, 3, 4, 5};auto& element = getElement(arr, 2);// 修改引用会影响原数组element = 30;std::cout << "arr[2] = " << arr[2] << std::endl;  // 输出30return 0;
}

带有decltype的统一初始化语法

C++11引入了统一初始化语法,结合decltype可以轻松地创建与已有变量相同类型的新变量:

#include <iostream>
#include <vector>
#include <complex>int main() {std::vector<int> vec1 = {1, 2, 3};decltype(vec1) vec2 = {4, 5, 6};std::complex<double> c1(1.0, 2.0);decltype(c1) c2{3.0, 4.0};std::cout << "vec2: ";for (const auto& elem : vec2) {std::cout << elem << " ";}std::cout << std::endl;std::cout << "c2: " << c2 << std::endl;return 0;
}

元函数中的类型计算

在元编程中,decltype可以用于计算类型,例如创建类型特性或模板元函数:

#include <iostream>
#include <type_traits>// 检查一个类型是否可比较
template<typename T, typename = void>
struct is_comparable : std::false_type {};template<typename T>
struct is_comparable<T, typename std::enable_if<true, decltype(std::declval<T>() == std::declval<T>(), void())>::type
> : std::true_type {};// 获取迭代器的值类型
template<typename Iterator>
struct iterator_value_type {using type = typename std::remove_reference<decltype(*std::declval<Iterator>())>::type;
};int main() {std::cout << "int is comparable: " << std::boolalpha << is_comparable<int>::value << std::endl;std::cout << "vector<int> is comparable: " << std::boolalpha << is_comparable<std::vector<int>>::value << std::endl;using it_type = std::vector<std::string>::iterator;using value_type = iterator_value_type<it_type>::type;std::cout << "vector<string>::iterator value type is string? " << std::is_same<value_type, std::string>::value << std::endl;return 0;
}

C++14中的decltype(auto)

C++14引入了decltype(auto)作为一种新的推导方式,它的工作原理是:

  1. 使用auto部分来表示我们想要类型推导
  2. 使用decltype规则而不是auto规则进行推导

这解决了C++11中使用auto无法推导引用类型和保留cv限定符的问题:

#include <iostream>
#include <vector>
#include <type_traits>// 返回一个引用的函数
int& getRef() {static int x = 42;return x;
}// C++11风格的完美转发函数
template<typename F, typename... Args>
auto perfectForward11(F&& f, Args&&... args) -> decltype(std::forward<F>(f)(std::forward<Args>(args)...)) {return std::forward<F>(f)(std::forward<Args>(args)...);
}// C++14风格的完美转发函数,更简洁
template<typename F, typename... Args>
decltype(auto) perfectForward14(F&& f, Args&&... args) {return std::forward<F>(f)(std::forward<Args>(args)...);
}int main() {int x = 10;// auto vs decltype(auto)auto a1 = x;                // intdecltype(auto) a2 = x;      // intauto a3 = (x);              // intdecltype(auto) a4 = (x);    // int&,因为(x)是左值表达式// 使用引用返回函数auto r1 = getRef();         // intdecltype(auto) r2 = getRef(); // int&// 验证类型std::cout << "a1 is reference? " << std::boolalpha << std::is_reference<decltype(a1)>::value << std::endl;  // falsestd::cout << "a2 is reference? " << std::boolalpha << std::is_reference<decltype(a2)>::value << std::endl;  // falsestd::cout << "a3 is reference? " << std::boolalpha << std::is_reference<decltype(a3)>::value << std::endl;  // falsestd::cout << "a4 is reference? " << std::boolalpha << std::is_reference<decltype(a4)>::value << std::endl;  // truestd::cout << "r1 is reference? " << std::boolalpha << std::is_reference<decltype(r1)>::value << std::endl;  // falsestd::cout << "r2 is reference? " << std::boolalpha << std::is_reference<decltype(r2)>::value << std::endl;  // true// 完美转发函数使用std::vector<int> vec = {1, 2, 3};auto& elem1 = perfectForward11(getRef, vec[0]);auto& elem2 = perfectForward14(getRef, vec[0]);elem1 = 100;elem2 = 200;std::cout << "vec[0] = " << vec[0] << std::endl;  // 200return 0;
}

decltype(auto)在泛型编程中特别有用,它能确保完美转发和函数返回类型推导时保留精确的类型特性。

函数返回类型的使用

decltype(auto)特别适合用于函数返回类型的推导,尤其是需要完美转发返回值时:

#include <iostream>
#include <vector>
#include <type_traits>template<typename Container>
decltype(auto) getFirst(Container& container) {return container[0];  // 返回容器第一个元素的精确类型(包括引用)
}template<typename T>
decltype(auto) forwardValue(T&& value) {return std::forward<T>(value);  // 完美转发,保留value的全部类型信息
}int main() {// 使用容器std::vector<int> numbers = {1, 2, 3, 4, 5};decltype(auto) first = getFirst(numbers);static_assert(std::is_same<decltype(first), int&>::value, "first should be int&");first = 100;  // 修改引用std::cout << "numbers[0] = " << numbers[0] << std::endl;  // 100// 使用转发函数int x = 42;const int cx = x;decltype(auto) rx = forwardValue(x);      // int&decltype(auto) crx = forwardValue(cx);    // const int&decltype(auto) rrx = forwardValue(10);    // int&&// 验证类型std::cout << "rx is int&? " << std::boolalpha << std::is_same<decltype(rx), int&>::value << std::endl;std::cout << "crx is const int&? " << std::boolalpha << std::is_same<decltype(crx), const int&>::value << std::endl;std::cout << "rrx is int&&? " << std::boolalpha << std::is_same<decltype(rrx), int&&>::value << std::endl;return 0;
}

这种用法在编写通用库和模板代码时非常有用,能够减少冗长的返回类型声明,同时保证类型安全。

实际应用示例

示例1:通用容器访问函数

#include <iostream>
#include <vector>
#include <map>
#include <string>// 通用的容器访问函数,正确处理返回类型(包括引用)
template<typename Container>
decltype(auto) access(Container& c, size_t index) {return c[index];
}// 特化版本,处理std::map
template<typename Key, typename Value>
Value& access(std::map<Key, Value>& m, const Key& key) {return m[key];
}int main() {// 向量示例std::vector<int> numbers = {10, 20, 30, 40, 50};// 修改元素access(numbers, 2) = 300;std::cout << "Modified vector: ";for (const auto& n : numbers) {std::cout << n << " ";}std::cout << std::endl;// 映射示例std::map<std::string, int> ages = {{"Alice", 25},{"Bob", 30},{"Charlie", 35}};// 修改或添加元素access(ages, "Alice") = 26;access(ages, "David") = 40;  // 新增元素std::cout << "Modified map:" << std::endl;for (const auto& [name, age] : ages) {std::cout << name << ": " << age << std::endl;}return 0;
}

示例2:泛型算法

#include <iostream>
#include <vector>
#include <list>
#include <algorithm>
#include <numeric>// 通用的求和函数,能处理不同容器和元素类型
template<typename Container>
auto sumElements(const Container& c) -> decltype(std::accumulate(std::begin(c), std::end(c), typename Container::value_type{})) {return std::accumulate(std::begin(c), std::end(c), typename Container::value_type{});
}// 获取容器中的最大元素(返回引用)
template<typename Container>
decltype(auto) getMaxElement(Container& c) {auto maxIt = std::max_element(std::begin(c), std::end(c));return *maxIt;  // 返回最大元素的引用
}int main() {std::vector<int> intVec = {1, 2, 3, 4, 5};std::list<double> doubleList = {1.1, 2.2, 3.3, 4.4, 5.5};// 求和auto intSum = sumElements(intVec);          // intauto doubleSum = sumElements(doubleList);   // doublestd::cout << "Sum of integers: " << intSum << std::endl;std::cout << "Sum of doubles: " << doubleSum << std::endl;// 获取并修改最大元素auto& maxInt = getMaxElement(intVec);auto& maxDouble = getMaxElement(doubleList);std::cout << "Max integer before: " << maxInt << std::endl;std::cout << "Max double before: " << maxDouble << std::endl;maxInt = 100;maxDouble = 100.1;std::cout << "Max integer after: " << maxInt << std::endl;std::cout << "Max double after: " << maxDouble << std::endl;// 验证修改是否生效std::cout << "Modified vector: ";for (const auto& n : intVec) {std::cout << n << " ";}std::cout << std::endl;std::cout << "Modified list: ";for (const auto& n : doubleList) {std::cout << n << " ";}std::cout << std::endl;return 0;
}

示例3:高级类型特性

#include <iostream>
#include <type_traits>
#include <vector>
#include <map>// 获取表达式的值类别(左值、右值等)
template<typename T>
struct value_category {static constexpr const char* get() {if (std::is_lvalue_reference<T>::value) {return "lvalue";} else if (std::is_rvalue_reference<T>::value) {return "rvalue";} else {return "prvalue";}}
};// 通用打印函数,显示表达式类型
template<typename T>
void printExpressionType(const char* expr, T&& value) {using ExactType = decltype(std::forward<T>(value));std::cout << "Expression: " << expr << "\n"<< "  - Type: " << typeid(std::decay_t<ExactType>).name() << "\n"<< "  - Category: " << value_category<ExactType>::get() << "\n"<< "  - Is reference? " << std::boolalpha << std::is_reference<ExactType>::value << "\n"<< "  - Is const? " << std::is_const<std::remove_reference_t<ExactType>>::value << std::endl;
}// 获取容器元素类型的通用方法
template<typename Container>
struct container_traits {using value_type = typename std::decay_t<Container>::value_type;using reference = decltype(*std::begin(std::declval<Container&>()));using iterator = decltype(std::begin(std::declval<Container&>()));
};int main() {int x = 42;const int cx = x;int& rx = x;// 分析各种表达式的类型printExpressionType("x", x);printExpressionType("cx", cx);printExpressionType("rx", rx);printExpressionType("(x)", (x));printExpressionType("std::move(x)", std::move(x));printExpressionType("42", 42);// 分析容器类型特性using VecTraits = container_traits<std::vector<int>>;using MapTraits = container_traits<std::map<std::string, double>>;std::cout << "\nVector traits:" << std::endl;std::cout << "  - value_type: " << typeid(VecTraits::value_type).name() << std::endl;std::cout << "  - reference: " << typeid(VecTraits::reference).name() << std::endl;std::cout << "  - iterator: " << typeid(VecTraits::iterator).name() << std::endl;std::cout << "\nMap traits:" << std::endl;std::cout << "  - value_type: " << typeid(MapTraits::value_type).name() << std::endl;std::cout << "  - reference: " << typeid(MapTraits::reference).name() << std::endl;std::cout << "  - iterator: " << typeid(MapTraits::iterator).name() << std::endl;return 0;
}

最佳实践与注意事项

何时使用decltype

以下是使用decltype的推荐场景:

  1. 需要精确保留类型信息,包括引用、const和volatile限定符时:

    template<typename T>
    auto getRef(T& obj) -> decltype(obj) {return obj;  // 返回精确的引用类型
    }
    
  2. 模板函数返回类型依赖于参数类型时:

    template<typename T, typename U>
    auto add(T t, U u) -> decltype(t + u) {return t + u;
    }
    
  3. 需要从表达式获取确切类型来声明变量时:

    auto func() {std::vector<int> vec = {1, 2, 3};decltype(vec[0]) elem = vec[0];  // elem的类型是int&// ...
    }
    
  4. 元编程和类型特性中需要对类型进行操作时:

    template<typename Container>
    struct element_type {using type = std::remove_reference_t<decltype(*std::begin(std::declval<Container>()))>;
    };
    

避免常见陷阱

  1. 额外括号导致的引用类型

    int x = 10;
    decltype(x) a = 20;    // a的类型是int
    decltype((x)) b = x;   // b的类型是int&,注意额外的括号!
    
  2. decltype与函数调用

    std::vector<int> getVector();
    decltype(getVector()) vec;  // 正确,vec的类型是std::vector<int>,不会调用getVector()
    decltype(getVector()[0]) elem = 10;  // 错误!getVector()[0]需要调用getVector()
    
  3. decltype与auto结合使用

    int x = 10;
    auto a = (x);       // a的类型是int
    decltype(auto) b = (x);  // b的类型是int&,注意差异!
    

风格建议

  1. 在复杂表达式上使用decltype时添加注释

    // 返回容器元素的引用类型
    template<typename Container>
    decltype(auto) getFirst(Container& c) {return c[0];
    }
    
  2. 避免对复杂嵌套表达式使用decltype,可能导致难以预测的类型:

    // 不好的做法:复杂表达式使用decltype
    decltype(foo().bar().member->func()) x;// 更好的做法:拆分为多个易于理解的步骤
    auto temp = foo().bar();
    using ResultType = decltype(temp.member->func());
    ResultType x;
    
  3. 考虑使用类型别名简化复杂类型

    template<typename Iterator>
    using value_type_t = std::remove_reference_t<decltype(*std::declval<Iterator>())>;
    

总结

decltype关键字是C++11中引入的强大类型推导工具,它允许我们在不实际计算表达式的情况下获取表达式的精确类型。与auto不同,decltype能够保留引用、const和volatile限定符,使其特别适合用于泛型编程和模板元编程。C++14引入的decltype(auto)则进一步简化了类型推导,特别是在需要完美转发和保留表达式精确类型的场景下,极大地提高了代码的灵活性和表达力。要有效使用decltypedecltype(auto),需要理解它们的类型推导规则,特别是对标识符、括号表达式和左值表达式的处理。同时,应当避免在过于复杂的表达式上使用decltype,以确保代码的可读性和可维护性。在现代C++中,decltype已经成为泛型编程和模板库开发的关键工具,与auto、类型特性和其他模板技术结合使用,可以构建出非常强大和灵活的代码。在下一篇文章中,我们将探讨C++11/14中的列表初始化特性,它如何简化对象的初始化过程,以及在现代C++编程中的广泛应用。


这是我C++学习之旅系列的第四十二篇技术文章。查看完整系列目录了解更多内容。

相关文章:

C++学习:六个月从基础到就业——C++11/14:decltype关键字

C学习&#xff1a;六个月从基础到就业——C11/14&#xff1a;decltype关键字 本文是我C学习之旅系列的第四十二篇技术文章&#xff0c;也是第三阶段"现代C特性"的第四篇&#xff0c;主要介绍C11/14中的decltype关键字。查看完整系列目录了解更多内容。 引言 在现代C…...

【51】快速获取数码管段选表(含小数点)及字母表的工具(分享)

1 介绍 1.1 画面 1.2 用法 输入IO口和段码字母的映射关系&#xff0c;比如这里e4d5dp2&#xff0c;指的是bit4是e段&#xff0c;bit5是d段&#xff0c;bit2是小数点dp段。 然后选择有效电平&#xff08;1表示亮 or 0表示亮&#xff09;。 点击生成段码配置&#xff0c;即可得到…...

高频面试题(含笔试高频算法整理)基本总结回顾120

干货分享&#xff0c;感谢您的阅读&#xff01; &#xff08;暂存篇---后续会删除&#xff0c;完整版和持续更新见高频面试题基本总结回顾&#xff08;含笔试高频算法整理&#xff09;&#xff09; 备注&#xff1a;引用请标注出处&#xff0c;同时存在的问题请在相关博客留言…...

5月17日

这几天不知道为啥没更新。可能是玩得太疯了。或者是考试有点集中&#xff1f;&#xff1f; 线性代数开课了&#xff0c;英语昨天完成了debate 昨天中午debate结束我们就出去玩了&#xff0c;去的那里时光民俗&#xff0c;别墅很好&#xff0c;770平米&#xff0c;但是缺点是可…...

摩方 12 代 N200 迷你主机(Ubuntu 系统)WiFi 抓包环境配置教程

摩方12代N200迷你主机标配 Intel AX201无线网卡&#xff0c;支持 WiFi 6 协议&#xff08;802.11ax&#xff09;及蓝牙5.2。此网卡兼容主流抓包工具&#xff0c;但需注意&#xff1a; 驱动兼容性&#xff1a;Ubuntu 20.04及以上内核版本&#xff08;5.4&#xff09;默认支持AX2…...

从零开始:使用 PyTorch 构建深度学习网络

从零开始&#xff1a;使用 PyTorch 构建深度学习网络 目录 PyTorch 简介环境配置PyTorch 基础构建神经网络训练模型评估与测试案例实战&#xff1a;手写数字识别进阶技巧常见问题解答 PyTorch 简介 PyTorch 是一个开源的深度学习框架&#xff0c;由 Facebook&#xff08;现…...

应用层自定义协议与序列化

应用层自定义协议与序列化 应用层协议网络版计算器序列化和反序列化序列化反序列化 重新理解read、write、recv、send和TCP为什么支持全双工代码结构Jsoncpp特性安装序列化使用Json::Value的toStyledString方法使用Json::StreamWriter使用Json::FastWriter 反序列化使用Json::R…...

2025春训第二十场

问题 B: 狗是啥呀 题目描述 在神秘的地狱深处&#xff0c;有着一种神秘的犬类生物&#xff0c;据传这种生物长了x个脑袋&#xff0c;并且具有强大的生命力。由于见过它的人全都下落不明&#xff0c;至今没有人知道它的真面目。 一位勇士为了斩杀这奇怪的生物&#xff0c;来到地…...

分糖果--思维+while判断

1.从左到右只考虑右边一遍&#xff0c;再从右到左考虑左边一遍&#xff0c;相当于左右考虑了 2.然后关键是1遍不一定行&#xff0c;while循环直到成功 https://www.luogu.com.cn/problem/B4091 #include<bits/stdc.h> using namespace std; #define N 100011 typedef …...

[system-design] ByteByteGo_Note Summary

目录 通信协议 REST API 与 GraphQL gRPC 如何工作&#xff1f; 什么是Webhook&#xff1f; 如何提高应用程序接口的性能&#xff1f; HTTP 1.0 -> HTTP 1.1 -> HTTP 2.0 -> HTTP 3.0 (QUIC) SOAP vs REST vs GraphQL vs RPC 代码优先与应用程序接口优先 HTT…...

Flask项目实践:构建功能完善的博客系统(含评论与标签功能)

引言 在Python Web开发领域&#xff0c;Flask以其轻量级、灵活性和易用性赢得了众多开发者的青睐。本文将带您从零开始构建一个功能完善的博客系统&#xff0c;包含文章发布、评论互动和标签分类等核心功能。通过这个实战项目&#xff0c;您不仅能掌握Flask的核心技术&#xf…...

Python爬虫实战:获取1688商品信息

在电商领域&#xff0c;获取1688商品信息对于市场分析、竞品研究、用户体验优化等至关重要。1688作为国内领先的B2B电商平台&#xff0c;提供了丰富的商品资源。通过Python爬虫技术&#xff0c;我们可以高效地获取1688商品的详细信息&#xff0c;包括商品名称、价格、图片、描述…...

Canva 推出自有应用生成器以与 Bolt 和 Lovable 竞争

AI 目前是一个巨大的市场,每个人都想从中分一杯羹。 即使是 Canva,这个以拖放图形设计而闻名的流行设计平台,也在其 Canva Create 2025 活动中发布了自己版本的代码生成器,加入了 AI 竞赛。 但为什么一个以设计为先的平台会提供代码生成工具呢? 乍看之下,这似乎有些不…...

多平台屏幕江湖生存指南

UniApp 屏幕适配大师:多平台屏幕江湖生存指南 屏幕江湖:尺寸混战 屏幕适配就像是应对不同体型的客人:从迷你的手机屏,到标准的平板,再到巨大的电视屏幕,你的应用必须有如武林高手般的适应力。 ┌──────────────────────────────────…...

BootCDN介绍(Bootstrap主导的前端开源项目免费CDN加速服务)

文章目录 BootCDN前端开源项目CDN加速服务全解析什么是BootCDN技术原理与架构CDN技术基础BootCDN架构特点1. 全球分布式节点网络2. 智能DNS解析系统3. 高效缓存管理机制4. 自动同步更新机制5. HTTPS和HTTP/2协议支持 BootCDN的核心优势速度与稳定性开源免费资源丰富度技术规范遵…...

LeetCode 153. 寻找旋转排序数组中的最小值:二分查找法详解及高频疑问解析

文章目录 问题描述算法思路&#xff1a;二分查找法关键步骤 代码实现代码解释高频疑问解答1. 为什么循环条件是 left < right 而不是 left < right&#xff1f;2. 为什么比较 nums[mid] > nums[right] 而不是 nums[left] < nums[mid]&#xff1f;3. 为什么 right …...

刷leetcodehot100返航版--二叉树

二叉树理论基础 二叉树的种类 满二叉树和完全二叉树&#xff0c;二叉树搜索树 满二叉树 如果一棵二叉树只有度为0的结点和度为2的结点&#xff0c;并且度为0的结点在同一层上&#xff0c;则这棵二叉树为满二叉树。 节点个数2^n-1【n为树的深度】 完全二叉树 在完全二叉树…...

「Mac畅玩AIGC与多模态41」开发篇36 - 用 ArkTS 构建聚合搜索前端页面

一、概述 本篇基于上一节 Python 实现的双通道搜索服务&#xff08;聚合 SearxNG 本地知识库&#xff09;&#xff0c;构建一个完整的 HarmonyOS ArkTS 前端页面。用户可在输入框中输入关键词&#xff0c;实时查询本地服务 http://localhost:5001/search?q...&#xff0c;返…...

【LINUX操作系统】生产者消费者模型(下):封装、信号量与环形队列

1.封装、完善基于阻塞队列的productor-consumer module 前文中我们封装了自己的Mutex 【LINUX操作系统】线程同步与互斥-CSDN博客 按照老规矩&#xff0c;现在我们对同步与互斥的理解更进一步了&#xff0c;现在把这种面向过程的语言封装成面向对象的写法 1.1 封装条件变量 #p…...

项目管理学习-CSPM-4考试总结

前言 经过两个月左右时间的学习&#xff0c;今天&#xff08;2025年5月17日&#xff09;参加了CSPM-4的考试&#xff0c;仿佛回到了2011年参加软考高项的时候。中午12点考完出来后&#xff0c;手都是酸酸的。不过整体感觉还可以&#xff0c;和预想的差不多。CSPM-4的考试一共有…...

自己手写tomcat项目

一&#xff1a;Servlet的原理 在Servlet(接口中)有&#xff1a; 1.init():初始化servlet 2.getServletConfig()&#xff1a;获取当前servlet的配置信息 3.service():服务器&#xff08;在HttpServlet中实现&#xff0c;目的是为了更好的匹配http的请求方式&#xff09; 4.g…...

C语言—再学习(结构体)

一、建立结构体 用户自己建立由不同类型数据组成的组合型的数据结构&#xff0c;它称为结构体。 struct Student { int num; //学号char name[20]; //名字为字符串char sex; //性别int age; //年纪float score; //分数char addr[30]; 地址为字符…...

SpringBoot--自动配置原理详解

为什么要学习自动配置原理&#xff1f; 原因&#xff1a;在实际开发中&#xff0c;我们经常会定义一些公共的组件&#xff0c;提供各个团队来使用&#xff0c;为了使用方便&#xff0c;我们经常会将公共的组件自定义成starter&#xff0c;如果想自定义starter&#xff0c;必须…...

MiInsertPageInFreeList函数分析和MmFreePagesByColor数组的关系

第一部分&#xff1a; Color MI_GET_COLOR_FROM_LIST_ENTRY(PageFrameIndex, Pfn1); ColorHead &MmFreePagesByColor[ListName][Color]; 第二部分&#xff1a; #define MI_GET_COLOR_FROM_LIST_ENTRY(index,pfn) \ ((ULONG)(((pfn)->…...

Windows/MacOS WebStorm/IDEA 中开发 Uni-App 配置

文章目录 前言1. 安装 HBuilder X2. WebStorm/IDEA 安装 Uniapp Tool 插件3. 配置 Uniapp Tool 插件4. 运行 Uni-App 项目 前言 前端开发人员对 WebStorm 一定不陌生&#xff0c;但有时需要开发 Uni-App 的需求&#xff0c;就必须要采用 HBuilder X&#xff0c;如果不习惯 HBu…...

redisson分布式锁实现原理归纳总结

Redisson 分布式锁的实现原理主要依赖于 Redis 的 Hash 数据结构、Lua 脚本、发布订阅机制以及看门狗&#xff08;Watchdog&#xff09;机制&#xff0c;以下是核心要点总结&#xff1a; 1. 核心原理 • 互斥性与可重入性&#xff1a; 通过 Redis 的 Hash 数据结构保存锁的持…...

Ubuntu 添加系统调用

实验内容 通过内核编译法添加一个不用传递参数的系统调用&#xff0c;其功能可自定义。 &#xff08;1&#xff09;添加系统调用号&#xff0c;系统会根据这个号找到syscall_table中的相应表项。具体做法是在syscall_64.tbl文件中添加系统调用号和调用函数的对应关系。 &#…...

Olib 2.2.0 | 免费开源软件,无需注册登录即可从ZLibrary下载多语言电子书

Olib是一款专为书籍爱好者设计的免费开源软件&#xff0c;它允许用户无需注册或登录即可从ZLibrary高速下载各种语言的电子书。该软件支持上百种语言的电子书下载&#xff0c;非常适合需要多语言资源的读者和研究人员使用。Olib的操作界面非常直观&#xff0c;使得书籍的搜索与…...

c++动态链接库

1. 生成动态链接库 首先实现一个动态链接库的代码 // example.cpp #include <iostream> void sayHello() {std::cout << "Hello from shared library!" << std::endl; }int add(int a, int b) {return a b; }// example.h #pragma once void sa…...

HelloWorld

HelloWorld 新建一个java文件 文件后缀名为 .javahello.java【注意】系统可能没有显示文件后缀名&#xff0c;我们需要手动打开 编写代码 public class hello {public static void main(String[] args) {System.out.print(Hello,World)} }编译 javac java文件&#xff0c;会生…...

SVGPlay:一次 CodeBuddy 主动构建的动画工具之旅

我正在参加CodeBuddy「首席试玩官」内容创作大赛&#xff0c;本文所使用的 CodeBuddy 免费下载链接&#xff1a;腾讯云代码助手 CodeBuddy - AI 时代的智能编程伙伴 背景与想法 我一直对 SVG 图标的动画处理有浓厚兴趣&#xff0c;特别是描边、渐变、交互等效果能为图标增添许…...

SLAM定位常用地图对比示例

序号 地图类型 概述 1 格栅地图 将现实环境栅格化,每一个栅格用 0 和 1 分别表示空闲和占据状态,初始化为未知状态 0.5 2 特征地图 以点、线、面等几何特征来描绘周围环境,将采集的信息进行筛选和提取得到关键几何特征 3 拓扑地图 将重要部分抽象为地图,使用简单的图形表示…...

强化学习中,frames(帧)和 episodes(回合)

在强化学习中&#xff0c;frames&#xff08;帧&#xff09;和 episodes&#xff08;回合&#xff09;是两个不同的概念&#xff1a; 1. 定义差异 Frame&#xff08;帧&#xff09;&#xff1a; 表示智能体与环境交互的单个时间步&#xff08;step&#xff09;&#xff0c;例如…...

HCIP第六次作业

一、拓扑图 二、需求 1、使用PreVal策略&#xff0c;确保R4通过R2到达192.168.10.0/24 2、使用AS_Path策略&#xff0c;确保R4通过R3到达192.168.11.0/24 3、配置MED策略&#xff0c;确保R4通过R3到达192.168.12.0/24 4、使用Local Preference策略&#xff0c;确保R1通过R2…...

高频面试题(含笔试高频算法整理)基本总结回顾110

干货分享&#xff0c;感谢您的阅读&#xff01; &#xff08;暂存篇---后续会删除&#xff0c;完整版和持续更新见高频面试题基本总结回顾&#xff08;含笔试高频算法整理&#xff09;&#xff09; 备注&#xff1a;引用请标注出处&#xff0c;同时存在的问题请在相关博客留言…...

数据湖与数据仓库融合:Hudi、Iceberg、Delta Lake 实践对比

在实时与离线一体化的今天,数据湖与数据仓库边界不断融合,越来越多企业选用如 Hudi、Iceberg、Delta Lake 等开源方案实现统一的数据存储、计算、分析平台。本篇将围绕以下关键点,展开实战对比与解决方案分享: ✅ 实时写入能力 ✅ ACID 保证 ✅ 增量数据处理能力 ✅ 流批一…...

OGG 更新表频繁导致进程中断,见鬼了?非也!

大家好&#xff0c;这里是 DBA学习之路&#xff0c;专注于提升数据库运维效率。 目录 前言问题描述问题分析解决方案后续 前言 最近几周一直遇到一个 OGG 问题&#xff0c;有一张表已更新就会中断 OGG 同步进程&#xff0c;本文记录一下分析过程以及解决方案。 问题描述 昨天…...

C++学习-入门到精通-【7】类的深入剖析

C学习-入门到精通-【7】类的深入剖析 类的深入剖析 C学习-入门到精通-【7】类的深入剖析一、Time类的实例研究二、组成和继承三、类的作用域和类成员的访问类作用域和块作用域圆点成员选择运算符(.)和箭头成员选择运算符(->)访问函数和工具函数 四、具有默认实参的构造函数重…...

非易失性存储技术综合对比:EEPROM、NVRAM、NOR Flash、NAND Flash和SD卡

非易失性存储技术综合对比&#xff1a;EEPROM、NVRAM、NOR Flash、NAND Flash和SD卡 读写性能对比 存储类型读取速度写入速度随机访问能力最小操作单位NVRAM极快(~10ns)极快(~10ns)极优(字节级)字节EEPROM中等(~100ns)慢(~5ms/字节)优(字节级)字节NOR Flash快(~50ns)慢(~5ms/…...

数字化转型- 数字化转型路线和推进

数字化转型三个阶段 百度百科给出的企业的数字化转型包括信息化、数字化、数智化三个阶段 信息化是将企业在生产经营过程中产生的业务信息进行记录、储存和管理&#xff0c;通过电子终端呈现&#xff0c;便于信息的传播与沟通。数字化通过打通各个系统的互联互通&#xff0c;…...

ARM (Attention Refinement Module)

ARM模块【来源于BiSeNet】&#xff1a;细化特征图的注意力&#xff0c;增强重要特征并抑制不重要的特征。 Attention Refinement Module (ARM) 详解 ARM (Attention Refinement Module) 是 BiSeNet 中用于增强特征表示的关键模块&#xff0c;它通过注意力机制来细化特征图&…...

符合Python风格的对象(对象表示形式)

对象表示形式 每门面向对象的语言至少都有一种获取对象的字符串表示形式的标准方 式。Python 提供了两种方式。 repr()   以便于开发者理解的方式返回对象的字符串表示形式。str()   以便于用户理解的方式返回对象的字符串表示形式。 正如你所知&#xff0c;我们要实现_…...

AtCoder AT_abc406_c [ABC406C] ~

前言 除了 A 题&#xff0c;唯一一道一遍过的题。 题目大意 我们定义满足以下所有条件的一个长度为 N N N 的序列 A ( A 1 , A 2 , … , A N ) A(A_1,A_2,\dots,A_N) A(A1​,A2​,…,AN​) 为波浪序列&#xff1a; N ≥ 4 N\ge4 N≥4&#xff08;其实满足后面就必须满足这…...

多指标组合策略

该策略(MultiConditionStrategy)是一种基于多种技术指标和市场条件的交易策略。它通过综合考虑多个条件来生成交易信号,从而决定买入或卖出的时机。 以下是对该策略的详细分析: 交易逻辑思路 1. 条件1:星期几和价格变化判断 - 该条件根据当前日期是星期几以及价格的变化…...

系统架构-大数据架构设计

基础介绍 三大挑战&#xff1a; 如何处理非结构化和半结构化数据如何探索大数据复杂性、不确定性特征描述的刻画方法及大数据的系统建模数据异构性与决策异构性的关系对大数据知识发现与管理决策的影响 架构特征&#xff1a; 鲁棒性&#xff08;稳定性&#xff09;和容错性…...

R语言空间数据处理入门教程

我的课程《R语言空间数据处理入门教程》已重新恢复课程售卖&#xff0c;有需要的读者可以学习。 &#x1f447;点击下方链接&#xff08;文末“阅读原文”可直达&#xff09;&#xff0c;立即开启你的空间数据之旅&#xff1a; https://www.bilibili.com/cheese/play/ss13775…...

QT+EtherCAT 主站协议库—SOEM主站

SOEM 是 Simple Open EtherCAT Master Library 的缩写&#xff0c;是瑞典 rt-lab 提供 的一个开源 EtherCAT 主站协议库 。 SOEM 库使用 C 语言编写&#xff0c;可以在 windows 以及 Linux 平台上运行&#xff0c;并也可以方便地移植到嵌入式平台上。 SOEM 支持 CoE &#xff0…...

Java-反射(Reflection)

一&#xff1a;概述 &#xff08;1&#xff09;出现背景 &#xff08;2&#xff09;解决方案 &#xff08;3&#xff09;使用场景 业务开发用的少&#xff0c;框架使用的多&#xff0c;业务反射被认为是动态语言的关键 &#xff08;4&#xff09;与原方法对比 &#xff08;5…...

第一次经历项目上线

这几天没写csdn&#xff0c;因为忙着项目上线的问题&#xff0c;我这阶段改了非常多的前端bug哈哈哈哈&#xff0c;说几个比较好的bug思想&#xff01; 这个页面算是我遇到的比较大的bug&#xff0c;因为我一开始的逻辑都写好了&#xff0c;询价就是在点击快递公司弹出弹框的时…...

基于C#的MQTT通信实战:从EMQX搭建到发布订阅全解析

MQTT(Message Queueing Telemetry Transport) 消息队列遥测传输&#xff0c;在物联网领域应用的很广泛&#xff0c;它是基于Publish/Subscribe模式&#xff0c;具有简单易用&#xff0c;支持QoS&#xff0c;传输效率高的特点。 它被设计用于低带宽&#xff0c;不稳定或高延迟的…...