C++之std::tuple(二) : 揭秘底层实现原理

相关系列文章

C++之std::tuple(二) : 揭秘底层实现原理

C++三剑客之std::any(一) : 使用

C++之std::tuple(一) : 使用精讲(全)

C++三剑客之std::variant(一) : 使用

C++三剑客之std::variant(二):深入剖析

深入理解可变参数(va_list、std::initializer_list和可变参数模版)

std::apply源码分析

目录

1.std::tuple存储设计

2.std::tuple构造

3.std::tuple_size

4.std::get<>访问值

5.operator=

6._Equals

7.总结


之前的章节中讲解std::tuple的使用和一些注意事项,接下来我们就以vs2019的std::tuple的实现来讲解它的底层实现原理。

1.std::tuple存储设计

std::tuple存储的递归写法基于这样的思想:一个包含N(N>0)个元素的元组可以存储为一个元素(第1个元素,或者说是列表的头部)加上一个包含N-1个元素的元组(尾部),而包含0个元素的元组是单独的特殊情况。下面看一下示例:

std::tuple<bool, int, double, std::string>  a(true, 1, 3.0, "1112222");

因此,一个包含4个元素的元组a的递归构造如下:

1) 第一层递归。准备存储a的第一个元素true,剩下的是 std:tuple<int,double,std::string> 对象。

2) 第二层递归。准备存储a的第二个元素1,剩下的是 std::tuple<double,std::string> 对象。

3) 第三层递归。准备存储a第三个元素3.0, 剩下的是 std::tuple<std::string> 对象。

4) 第四层递归。准备存储a第四个元素"1112222",剩余的是std::tuple<>,遇到std::tuple<>递归结束。此时a的才开始存储元素,并且存储元素是从"1112222"开始,然后递归开始返回。

5) 返回到第3步的递归,存储duble类型的3.0。

6) 返回到第2步的递归,存储第int类型的1。

7) 返回到第1步的递归,存储第bool类型的true。

也就是说,std::tuple 的构造函数中,最后一个传入的元素最先构造,最先传入的元素最后一个构造,符合递归顺序,即入栈顺序。下面从源码角度,具体来看看 std::tuple 的实现。

template <class _This, class... _Rest>
class tuple<_This, _Rest...> : private tuple<_Rest...> { // recursive tuple definition
public:using _This_type = _This; //当前元素using _Mybase    = tuple<_Rest...>; //余下的元素//以下都是构造函数,罗列了部分template <class _Tag, class _This2, class... _Rest2, enable_if_t<is_same_v<_Tag, _STD _Exact_args_t>, int> = 0>constexpr tuple(_Tag, _This2&& _This_arg, _Rest2&&... _Rest_arg): _Mybase(_Exact_args_t{}, _STD forward<_Rest2>(_Rest_arg)...), _Myfirst(_STD forward<_This2>(_This_arg)) {}template <class _Tag, class _Tpl, size_t... _Indices, enable_if_t<is_same_v<_Tag, _STD _Unpack_tuple_t>, int> = 0>constexpr tuple(_Tag, _Tpl&& _Right, index_sequence<_Indices...>);template <class _Tag, class _Tpl, enable_if_t<is_same_v<_Tag, _STD _Unpack_tuple_t>, int> = 0>constexpr tuple(_Tag, _Tpl&& _Right): tuple(_Unpack_tuple_t{}, _STD forward<_Tpl>(_Right),make_index_sequence<tuple_size_v<remove_reference_t<_Tpl>>>{}) {}...//获取余下的元素类,不带constconstexpr _Mybase& _Get_rest() noexcept { // get reference to rest of elementsreturn *this;}//获取余下的元素类,带constconstexpr const _Mybase& _Get_rest() const noexcept { // get const reference to rest         of elementsreturn *this;}...//template <size_t _Index, class... _Types>friend constexpr tuple_element_t<_Index, tuple<_Types...>>& get(tuple<_Types...>& _Tuple) noexcept;template <size_t _Index, class... _Types>friend constexpr const tuple_element_t<_Index, tuple<_Types...>>& get(const tuple<_Types...>& _Tuple) noexcept;template <size_t _Index, class... _Types>friend constexpr tuple_element_t<_Index, tuple<_Types...>>&& get(tuple<_Types...>&& _Tuple) noexcept;template <size_t _Index, class... _Types>friend constexpr const tuple_element_t<_Index, tuple<_Types...>>&& get(const tuple<_Types...>&& _Tuple) noexcept;template <size_t _Index, class... _Types>friend constexpr auto&& _Tuple_get(tuple<_Types...>&& _Tuple) noexcept;template <class _Ty, class... _Types>friend constexpr _Ty& get(tuple<_Types...>& _Tuple) noexcept;template <class _Ty, class... _Types>friend constexpr const _Ty& get(const tuple<_Types...>& _Tuple) noexcept;template <class _Ty, class... _Types>friend constexpr _Ty&& get(tuple<_Types...>&& _Tuple) noexcept;template <class _Ty, class... _Types>friend constexpr const _Ty&& get(const tuple<_Types...>&& _Tuple) noexcept;//包装的当前的元素类,_Tuple_val<_This> _Myfirst; // the stored element    
};

        从上面的代码看到,std::tuple的递归继承用到了private继承,说明各个元素都是独立的,互相没有关系。std::tuple的元素之间是一种组合的关系。另外一个是private继承可以造成empty base最优化,这对致力于“对象尺寸最小化”的程序开发者而言,可能很重要。

        那么,tuple是如何存储其中的元素呢?

_Tuple_val<_This> _Myfirst; // the stored element

原来,它有个成员叫_Myfirst,它就是用来存储_This类型的变量的。你会看到_Myfirst的类型不是_This而是_Tuple_val<_This>,其实,_Tuple_val又是一个类模板,它的代码这里就不展开了,简而言之,它的作用是存储一个tuple中的变量。_Myfirst._Val才是真正的元素。从_Tuple_val的定义可以看出:

template <class _Ty>
struct _Tuple_val { // stores each value in a tupleconstexpr _Tuple_val() : _Val() {}template <class _Other>constexpr _Tuple_val(_Other&& _Arg) : _Val(_STD forward<_Other>(_Arg)) {}template <class _Alloc, class... _Other, enable_if_t<!uses_allocator_v<_Ty, _Alloc>, int> = 0>constexpr _Tuple_val(const _Alloc&, allocator_arg_t, _Other&&... _Arg) : _Val(_STD forward<_Other>(_Arg)...) {}template <class _Alloc, class... _Other,enable_if_t<conjunction_v<_STD uses_allocator<_Ty, _Alloc>,_STD is_constructible<_Ty, _STD allocator_arg_t, const _Alloc&, _Other...>>,int> = 0>constexpr _Tuple_val(const _Alloc& _Al, allocator_arg_t, _Other&&... _Arg): _Val(allocator_arg, _Al, _STD forward<_Other>(_Arg)...) {}template <class _Alloc, class... _Other,enable_if_t<conjunction_v<_STD uses_allocator<_Ty, _Alloc>,_STD negation<_STD is_constructible<_Ty, _STD allocator_arg_t, const _Alloc&, _Other...>>>,int> = 0>constexpr _Tuple_val(const _Alloc& _Al, allocator_arg_t, _Other&&... _Arg): _Val(_STD forward<_Other>(_Arg)..., _Al) {}_Ty _Val;
};

在std::tuple类中定义_Myfirst的权限是public的,所以对外面而言是直接访问元素值的。

通过上面的分析,a中定义的类和类的继承关系图如下所示:

class std::tuple<>;
class std::tuple<std::string>;
class std::tuple<double, std::string>;
class std::tuple<int, double, std::string>;
class std::tuple<bool, int, double, std::string>;

内存的分布图如下:

2.std::tuple构造

tuple的构造函数就是初始化_Myfirst和_MyBase,当然,_MyBase也要进行么一个过程,直到tuple<>。

//分类构造
template <class _Tag, class _This2, class... _Rest2, enable_if_t<is_same_v<_Tag, _STD _Exact_args_t>, int> = 0>constexpr tuple(_Tag, _This2&& _This_arg, _Rest2&&... _Rest_arg): _Mybase(_Exact_args_t{}, _STD forward<_Rest2>(_Rest_arg)...), _Myfirst(_STD forward<_This2>(_This_arg)) {}template <class _Tag, class _Tpl, size_t... _Indices, enable_if_t<is_same_v<_Tag, _STD _Unpack_tuple_t>, int> = 0>constexpr tuple(_Tag, _Tpl&& _Right, index_sequence<_Indices...>);template <class _Tag, class _Tpl, enable_if_t<is_same_v<_Tag, _STD _Unpack_tuple_t>, int> = 0>constexpr tuple(_Tag, _Tpl&& _Right): tuple(_Unpack_tuple_t{}, _STD forward<_Tpl>(_Right),make_index_sequence<tuple_size_v<remove_reference_t<_Tpl>>>{}) {}template <class _Tag, class _Alloc, class _This2, class... _Rest2,enable_if_t<is_same_v<_Tag, _STD _Alloc_exact_args_t>, int> = 0>constexpr tuple(_Tag, const _Alloc& _Al, _This2&& _This_arg, _Rest2&&... _Rest_arg): _Mybase(_Alloc_exact_args_t{}, _Al, _STD forward<_Rest2>(_Rest_arg)...),_Myfirst(_Al, allocator_arg, _STD forward<_This2>(_This_arg)) {}template <class _Tag, class _Alloc, class _Tpl, size_t... _Indices,enable_if_t<is_same_v<_Tag, _STD _Alloc_unpack_tuple_t>, int> = 0>constexpr tuple(_Tag, const _Alloc& _Al, _Tpl&& _Right, index_sequence<_Indices...>);template <class _Tag, class _Alloc, class _Tpl, enable_if_t<is_same_v<_Tag, _STD _Alloc_unpack_tuple_t>, int> = 0>constexpr tuple(_Tag, const _Alloc& _Al, _Tpl&& _Right): tuple(_Alloc_unpack_tuple_t{}, _Al, _STD forward<_Tpl>(_Right),make_index_sequence<tuple_size_v<remove_reference_t<_Tpl>>>{}) {}//根据入口参数的不同分派到不同的构造函数
template <class _This2, class... _Rest2,enable_if_t<conjunction_v<_STD _Tuple_perfect_val<tuple, _This2, _Rest2...>,_STD _Tuple_constructible_val<tuple, _This2, _Rest2...>>,int> = 0>constexpr explicit(_Tuple_conditional_explicit_v<tuple, _This2, _Rest2...>) tuple(_This2&& _This_arg,_Rest2&&... _Rest_arg) noexcept(_Tuple_nothrow_constructible_v<tuple, _This2, _Rest2...>) // strengthened: tuple(_Exact_args_t{}, _STD forward<_This2>(_This_arg), _STD forward<_Rest2>(_Rest_arg)...) {}tuple(const tuple&) = default;
tuple(tuple&&)      = default;tuple& operator=(const volatile tuple&) = delete;

        它还提供了默认拷贝构造函数和移动构造函数(移动语义是C++11中新增的特性,可以参考C++之std::move(移动语义)-CSDN博客)。其实,它还有很多构造函数,写起来挺热闹,无非就是用不同的方式为它赋初值,故省略。

        上面的构造函数是根据_tag的不同被分派到不同的构造函数,它叫标签派发。源码中定义了如下的标签:

struct _Exact_args_t {explicit _Exact_args_t() = default;
}; // tag type to disambiguate construction (from one arg per element)struct _Unpack_tuple_t {explicit _Unpack_tuple_t() = default;
}; // tag type to disambiguate construction (from unpacking a tuple/pair)struct _Alloc_exact_args_t {explicit _Alloc_exact_args_t() = default;
}; // tag type to disambiguate construction (from an allocator and one arg per element)struct _Alloc_unpack_tuple_t {explicit _Alloc_unpack_tuple_t() = default;
}; // tag type to disambiguate construction (from an allocator and unpacking a tuple/pair)

3.std::tuple_size

先看一下源码:

template <class _Ty, _Ty _Val>
struct integral_constant {static constexpr _Ty value = _Val;using value_type = _Ty;using type       = integral_constant;constexpr operator value_type() const noexcept {return value;}_NODISCARD constexpr value_type operator()() const noexcept {return value;}
};// TUPLE INTERFACE TO tuple
template <class... _Types>
struct tuple_size<tuple<_Types...>> : integral_constant<size_t, sizeof...(_Types)> {}; // size of tupletemplate <class _Ty1, class _Ty2>
struct tuple_size<pair<_Ty1, _Ty2>> : integral_constant<size_t, 2> {}; // size of pair

std::integral_constant 包装特定类型的静态常量。它是 C++ 类型特征的基类。

std::tuple_size利用sizeof...求得可变参数的个数。

4.std::get<>访问值

先看一下tuple_element,tuple_element是获取tuple的元素,包括_Myfirst和_MyBase,源码:

//tuple<>
template <size_t _Index>
struct _MSVC_KNOWN_SEMANTICS tuple_element<_Index, tuple<>> { // enforce bounds checkingstatic_assert(_Always_false<integral_constant<size_t, _Index>>, "tuple index out of bounds");
};//tuple的_index == 0
template <class _This, class... _Rest>
struct _MSVC_KNOWN_SEMANTICS tuple_element<0, tuple<_This, _Rest...>> { // select first elementusing type = _This;// MSVC assumes the meaning of _Ttype; remove or rename, but do not change semanticsusing _Ttype = tuple<_This, _Rest...>;
};//tuple的_index > 0
template <size_t _Index, class _This, class... _Rest>
struct _MSVC_KNOWN_SEMANTICS tuple_element<_Index, tuple<_This, _Rest...>>: tuple_element<_Index - 1, tuple<_Rest...>> {}; // recursive tuple_element definition//pair
template <size_t _Idx, class _Ty1, class _Ty2>
struct _MSVC_KNOWN_SEMANTICS tuple_element<_Idx, pair<_Ty1, _Ty2>> {static_assert(_Idx < 2, "pair index out of bounds");using type = conditional_t<_Idx == 0, _Ty1, _Ty2>;
};template <size_t _Index, class _Tuple>
using tuple_element_t = typename tuple_element<_Index, _Tuple>::type;

通过_Index获取tuple元素:

template <size_t _Index, class... _Types>
_NODISCARD constexpr tuple_element_t<_Index, tuple<_Types...>>& get(tuple<_Types...>& _Tuple) noexcept {using _Ttype = typename tuple_element<_Index, tuple<_Types...>>::_Ttype;return static_cast<_Ttype&>(_Tuple)._Myfirst._Val;
}template <size_t _Index, class... _Types>
_NODISCARD constexpr const tuple_element_t<_Index, tuple<_Types...>>& get(const tuple<_Types...>& _Tuple) noexcept {using _Ttype = typename tuple_element<_Index, tuple<_Types...>>::_Ttype;return static_cast<const _Ttype&>(_Tuple)._Myfirst._Val;
}template <size_t _Index, class... _Types>
_NODISCARD constexpr tuple_element_t<_Index, tuple<_Types...>>&& get(tuple<_Types...>&& _Tuple) noexcept {using _Ty    = tuple_element_t<_Index, tuple<_Types...>>;using _Ttype = typename tuple_element<_Index, tuple<_Types...>>::_Ttype;return static_cast<_Ty&&>(static_cast<_Ttype&>(_Tuple)._Myfirst._Val);
}template <size_t _Index, class... _Types>
_NODISCARD constexpr const tuple_element_t<_Index, tuple<_Types...>>&& get(const tuple<_Types...>&& _Tuple) noexcept {using _Ty    = tuple_element_t<_Index, tuple<_Types...>>;using _Ttype = typename tuple_element<_Index, tuple<_Types...>>::_Ttype;return static_cast<const _Ty&&>(static_cast<const _Ttype&>(_Tuple)._Myfirst._Val);
}

上述代码分析get<index>的流程:

1)通过_Index递归构造出类 tuple_element_t

2)   获取当前元素 _MyFirst.Val

通过_Ty获取tuple元素:

//辅助类
template <class _Ty, class _Tuple>
struct _Tuple_element {}; // backstop _Tuple_element definitiontemplate <class _This, class... _Rest>
struct _Tuple_element<_This, tuple<_This, _Rest...>> { // select first elementstatic_assert(!_Is_any_of_v<_This, _Rest...>, "duplicate type T in get<T>(tuple)");using _Ttype = tuple<_This, _Rest...>;
};template <class _Ty, class _This, class... _Rest>
struct _Tuple_element<_Ty, tuple<_This, _Rest...>> { // recursive _Tuple_element definitionusing _Ttype = typename _Tuple_element<_Ty, tuple<_Rest...>>::_Ttype;
};//通过_Ty  get
template <class _Ty, class... _Types>
_NODISCARD constexpr _Ty& get(tuple<_Types...>& _Tuple) noexcept {using _Ttype = typename _Tuple_element<_Ty, tuple<_Types...>>::_Ttype;return static_cast<_Ttype&>(_Tuple)._Myfirst._Val;
}template <class _Ty, class... _Types>
_NODISCARD constexpr const _Ty& get(const tuple<_Types...>& _Tuple) noexcept {using _Ttype = typename _Tuple_element<_Ty, tuple<_Types...>>::_Ttype;return static_cast<const _Ttype&>(_Tuple)._Myfirst._Val;
}template <class _Ty, class... _Types>
_NODISCARD constexpr _Ty&& get(tuple<_Types...>&& _Tuple) noexcept {using _Ttype = typename _Tuple_element<_Ty, tuple<_Types...>>::_Ttype;return static_cast<_Ty&&>(static_cast<_Ttype&>(_Tuple)._Myfirst._Val);
}template <class _Ty, class... _Types>
_NODISCARD constexpr const _Ty&& get(const tuple<_Types...>&& _Tuple) noexcept {using _Ttype = typename _Tuple_element<_Ty, tuple<_Types...>>::_Ttype;return static_cast<const _Ty&&>(static_cast<const _Ttype&>(_Tuple)._Myfirst._Val);
}

上述代码分析get<_Ty>的流程:

1)通过_Ty递归构造出类 _Tuple_element

2)   获取当前元素 _MyFirst.Val

5.operator=

tuple重载了赋值符号(=),这样,tuple之间是可以赋值的。

    tuple& operator=(const volatile tuple&) = delete;//以下是特殊情况是可以赋值的template <class _Myself = tuple, class _This2 = _This,enable_if_t<conjunction_v<_STD _Is_copy_assignable_no_precondition_check<_This2>,_STD _Is_copy_assignable_no_precondition_check<_Rest>...>,int> = 0>_CONSTEXPR20 tuple& operator=(_Identity_t<const _Myself&> _Right) noexcept(conjunction_v<is_nothrow_copy_assignable<_This2>, is_nothrow_copy_assignable<_Rest>...>) /* strengthened */ {_Myfirst._Val = _Right._Myfirst._Val;_Get_rest()   = _Right._Get_rest();return *this;}template <class _Myself = tuple, class _This2 = _This,enable_if_t<conjunction_v<_STD _Is_move_assignable_no_precondition_check<_This2>,_STD _Is_move_assignable_no_precondition_check<_Rest>...>,int> = 0>_CONSTEXPR20 tuple& operator=(_Identity_t<_Myself&&> _Right) noexcept(conjunction_v<is_nothrow_move_assignable<_This2>, is_nothrow_move_assignable<_Rest>...>) {_Myfirst._Val = _STD forward<_This>(_Right._Myfirst._Val);_Get_rest()   = _STD forward<_Mybase>(_Right._Get_rest());return *this;}template <class... _Other, enable_if_t<conjunction_v<_STD negation<_STD is_same<tuple, _STD tuple<_Other...>>>,_STD _Tuple_assignable_val<tuple, const _Other&...>>,int> = 0>_CONSTEXPR20 tuple& operator=(const tuple<_Other...>& _Right) noexcept(_Tuple_nothrow_assignable_v<tuple, const _Other&...>) /* strengthened */ {_Myfirst._Val = _Right._Myfirst._Val;_Get_rest()   = _Right._Get_rest();return *this;}template <class... _Other, enable_if_t<conjunction_v<_STD negation<_STD is_same<tuple, _STD tuple<_Other...>>>,_STD _Tuple_assignable_val<tuple, _Other...>>,int> = 0>_CONSTEXPR20 tuple& operator=(tuple<_Other...>&& _Right) noexcept(_Tuple_nothrow_assignable_v<tuple, _Other...>) /* strengthened */ {_Myfirst._Val = _STD forward<typename tuple<_Other...>::_This_type>(_Right._Myfirst._Val);_Get_rest()   = _STD forward<typename tuple<_Other...>::_Mybase>(_Right._Get_rest());return *this;}template <class _First, class _Second,enable_if_t<_Tuple_assignable_v<tuple, const _First&, const _Second&>, int> = 0>_CONSTEXPR20 tuple& operator=(const pair<_First, _Second>& _Right) noexcept(_Tuple_nothrow_assignable_v<tuple, const _First&, const _Second&>) /* strengthened */ {_Myfirst._Val             = _Right.first;_Get_rest()._Myfirst._Val = _Right.second;return *this;}

赋值符号返回左边的引用,这种行为和C++的内置类型是一致的。_Get_rest是tuple的成员函数,作用是把除了_Myfirst之外的那些元素拿出来。

6._Equals

    //tuple内置函数template <class... _Other>constexpr bool _Equals(const tuple<_Other...>& _Right) const {return _Myfirst._Val == _Right._Myfirst._Val && _Mybase::_Equals(_Right._Get_rest());}//重载operator==template <class... _Types1, class... _Types2>
_NODISCARD constexpr bool operator==(const tuple<_Types1...>& _Left, const tuple<_Types2...>& _Right) {static_assert(sizeof...(_Types1) == sizeof...(_Types2), "cannot compare tuples of different sizes");return _Left._Equals(_Right);
}

其中进行了静态断言,如果两个tuple的元素个数不相同,会引发一个编译时的错误。如果对应的类型不能用==进行比较,在模板特化时也会引发编译期的错误,例如,tuple<std::string, int>不能和tuple<int, char>比较,因为std::string和int是不能用==进行比较的。

7.总结

终于写完了,也是想了好久才写的,涉及到的知识点主要是模版推导。写的不好的地方,欢迎批评指正;如果觉得好,点个赞,收藏关注!

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

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

相关文章

《论文阅读》利用提取的情感原因提高共情对话生成的内容相关性 CCL 2022

《论文阅读》利用提取的情感原因提高共情对话生成的内容相关性 前言简介模型架构情绪识别情绪原因提取实验结果示例总结前言 亲身阅读感受分享,细节画图解释,再也不用担心看不懂论文啦~ 无抄袭,无复制,纯手工敲击键盘~ 今天为大家带来的是《Using Extracted Emotion Caus…

module ‘json‘ has no attribute ‘dumps‘

如果在使用Python的json模块时遇到AttributeError: module json has no attribute dumps错误&#xff0c;通常是因为在Python环境中json模块不支持dumps方法。这种情况可能是因为Python的json模块被重命名或修改过导致的。 解决方法可以尝试以下几种&#xff1a; 1.检查Pytho…

从docx提取文本的Python实战代码

大家好,我是爱编程的喵喵。双985硕士毕业,现担任全栈工程师一职,热衷于将数据思维应用到工作与生活中。从事机器学习以及相关的前后端开发工作。曾在阿里云、科大讯飞、CCF等比赛获得多次Top名次。现为CSDN博客专家、人工智能领域优质创作者。喜欢通过博客创作的方式对所学的…

2.23 Qt day4 事件机制+定时器事件+键盘事件+鼠标事件

思维导图&#xff1a; 做一个闹钟&#xff0c;在行编辑器里输入定闹钟的时间&#xff0c;时间到了就语音播报文本里的内容&#xff0c;播报五次 widget.h&#xff1a; #ifndef WIDGET_H #define WIDGET_H#include <QWidget> #include<QDebug>//输出类 #include<…

使用matlab对探空站IGRAv2数据进行提取

目录 1.IGRA的下载 2.以CHM00050527-data.txt数据为例&#xff1a; 3.使用matlab进行数据提取&#xff1a; 4.mat文件的构成如下图&#xff1a; 5. Matlab 获取代码可关注微信公众号WZZHHH&#xff0c;或者咸鱼关注&#xff1a;WZZHHH123 6.部分代码展示&#xff1a; 1.I…

用什么软件制作电子杂志

想要制作高大上的电子杂志&#xff1f;别再烦恼啦&#xff01;今天给大家推荐一款超级实用的软件&#xff0c;让你轻松制作出专业水准的电子杂志&#xff01; 这款软件功能强大&#xff0c;操作简单&#xff0c;适合所有对设计感兴趣的小伙伴们。无论是新手还是专业设计师&…

神经网络系列---感知机(Neuron)

文章目录 感知机(Neuron)感知机(Neuron)的决策函数可以表示为&#xff1a;感知机(Neuron)的学习算法主要包括以下步骤&#xff1a;感知机可以实现逻辑运算中的AND、OR、NOT和异或(XOR)运算。 感知机(Neuron) 感知机(Neuron)是一种简单而有效的二分类算法&#xff0c;用于将输入…

Cenos7搭建gitLib服务器(全网最详细)

vm安装Cenos7可参考以下链接 VM安装Cenos7(全网超详细保姆教程)_vm centos7安装教程-CSDN博客 一.安装并配置必要的依赖关系 1.1安装依赖包 安装curl、policycoreutils-python、openssh-server依赖包 yum install -y curl policycoreutils-python openssh-server 在这个…

【嵌入式实践】【芝麻】【目录】从0到1给电动车添加指纹锁

0. 前言 该项目是基于stm32F103和指纹模块做了一个通过指纹锁控制电动车的小工具。支持添加指纹、删除指纹&#xff0c;电动车进入P档等待时计时&#xff0c;计时超过5min则自动锁车&#xff0c;计时过程中按刹车可中断P档状态&#xff0c;同时中断锁车计时。改项目我称之为“芝…

Zookeeper客户端命令、JAVA API、监听原理、写数据原理以及案例

1. Zookeeper节点信息 指定服务端&#xff0c;启动客户端命令&#xff1a; bin/zkCli.sh -server 服务端主机名:端口号 1&#xff09;ls / 查看根节点下面的子节点 ls -s / 查看根节点下面的子节点以及根节点详细信息 其中&#xff0c;cZxid是创建节点的事务id&#xff0c…

【Java程序设计】【C00302】基于Springboot的校园失物招领管理系统(有论文)

基于Springboot的校园失物招领管理系统&#xff08;有论文&#xff09; 项目简介项目获取开发环境项目技术运行截图 项目简介 这是一个基于Springboot的校园失物招领网站&#xff0c;本系统有管理员以及用户二种角色权限&#xff1b; 系统整体功能有&#xff1a;操作日志管理、…

[晓理紫]每日论文分享(有中文摘要,源码或项目地址)--大模型、扩散模型

专属领域论文订阅 VX 关注{晓理紫}&#xff0c;每日更新论文&#xff0c;如感兴趣&#xff0c;请转发给有需要的同学&#xff0c;谢谢支持 如果你感觉对你有所帮助&#xff0c;请关注我&#xff0c;每日准时为你推送最新论文。 分类: 大语言模型LLM视觉模型VLM扩散模型视觉语言…

5分钟轻松帮你EasyRecovery恢复女朋友照片

相信有不少男性电脑玩家都会将女朋友的照片存放在电脑硬盘之内&#xff0c;作为珍贵的收藏和回忆。但是在某些时候&#xff0c;如果我们错误地删除了这些照片&#xff0c;或者由于系统问题导致其中的照片丢失&#xff0c;那么我们怎么找回女朋友的照片&#xff1f;这个问题就足…

【统计分析数学模型】聚类分析: 系统聚类法

【统计分析数学模型】聚类分析&#xff1a; 系统聚类法 一、聚类分析1. 基本原理2. 距离的度量&#xff08;1&#xff09;变量的测量尺度&#xff08;2&#xff09;距离&#xff08;3&#xff09;R语言计算距离 三、聚类方法1. 系统聚类法2. K均值法 三、示例1. Q型聚类&#x…

用c# 自己封装的Modbus工具类库源码

前言 Modbus通讯协议在工控行业的应用是很多的&#xff0c;并且也是上位机开发的基本技能之一。相关的类库也很多也很好用。以前只负责用&#xff0c;对其并没有深入学习和了解。前段时间有点空就在这块挖了挖。想做到知其然还要知其所以然。所以就有了自己封装的Modbus工具类库…

28V270V航空交直流线缆:满足飞机对高质量电气连接的需求

28V/270V航空交直流线缆&#xff1a;航空业的“神经系统” 在现代航空业中&#xff0c;无论是飞机、直升机还是其他飞行器&#xff0c;都离不开一种重要的设备&#xff0c;那就是航空28V/270V航空交直流线缆。航空28V/270V航空交直流线缆是飞行器上的电气系统的重要组成部分&am…

6、进程、服务管理

一、进程管理 1.概述 进程是正在执行的程序或命令&#xff0c;每一个进程都独立运行&#xff0c;都有自己的地址空间&#xff0c;并占用一定的系统资源以后开发会遇见&#xff1a; 端口占用出现程序假死、卡死 2.查看系统运行进程 语法 ps 参数ps –a:显示当前终端下的所有…

#FPGA(基础知识)

1.IDE:Quartus II 2.设备&#xff1a;Cyclone II EP2C8Q208C8N 3.实验&#xff1a;正点原子-verilog基础知识 4.时序图&#xff1a; 5.步骤 6.代码&#xff1a;

Spring Session:Redis序列化配置|Session事件监听

Spring Session是可配置的。 Redis Configuration JSON序列化配置 Spring Session默认使用Java对象序列化机制对Session属性值进行序列化。 预定义类SysUser 先来定义一个SysUser类&#xff0c;在下面的演示案例中会用到。 package com.example.demo.model.entity;import j…

Sqli-labs靶场第11关详解[Sqli-labs-less-11]

Sqli-labs-Less-11 前言&#xff1a; SQL注入的三个条件&#xff1a; ①参数可控&#xff1b;&#xff08;从参数输入就知道参数可控&#xff09; ②参数过滤不彻底导致恶意代码被执行&#xff1b;&#xff08;需要在测试过程中判断&#xff09; ③参数带入数据库执行。&…