From 8fdca49e1d63db1075a23958ff23189818a49d6c Mon Sep 17 00:00:00 2001 From: muqiuhan Date: Thu, 13 Mar 2025 09:18:34 +0000 Subject: deploy: 11fd5b5345c11055a7c18e9b2c0baa4d2787fe15 --- .../index.html" | 2 +- "2023/05/04/C-\347\232\204-Trait/index.html" | 294 --------------------- .../index.html" | 279 ------------------- .../index.html" | 274 ------------------- .../index.html" | 2 +- .../index.html" | 10 +- 6 files changed, 7 insertions(+), 854 deletions(-) delete mode 100644 "2023/05/04/C-\347\232\204-Trait/index.html" delete mode 100644 "2023/05/11/C-vector-\347\232\204-push-back-\345\222\214-emplace-back/index.html" delete mode 100644 "2023/05/12/C-20-\345\256\236\347\216\260-string-split/index.html" (limited to '2023') diff --git "a/2023/05/03/Rust-NewType-\346\250\241\345\274\217/index.html" "b/2023/05/03/Rust-NewType-\346\250\241\345\274\217/index.html" index 7ad16dd5..dcb2a4e8 100644 --- "a/2023/05/03/Rust-NewType-\346\250\241\345\274\217/index.html" +++ "b/2023/05/03/Rust-NewType-\346\250\241\345\274\217/index.html" @@ -198,7 +198,7 @@
- Prev + Prev
diff --git "a/2023/05/04/C-\347\232\204-Trait/index.html" "b/2023/05/04/C-\347\232\204-Trait/index.html" deleted file mode 100644 index e626505f..00000000 --- "a/2023/05/04/C-\347\232\204-Trait/index.html" +++ /dev/null @@ -1,294 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - -C++ 的 Trait | 暮秋小屋 - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
-
- -
- -
-
-
- - - -
-
-
- - -
-
-
- - -
- -
- -
- -
-
-
-
-

C++ 的 traits 技术,是一种约定俗称的技术方案,用来为同一类数据(包括自定义数据类型和内置数据类型)提供统一的类型名(traits),这样可以统一的操作函数,例如 advance(), swap(), encode()/decode() 等。

-
-

例如,拥有义类型Foo, Bar,以及编译器自带类型 int, double, string,我们想要为这些不同的类型提供统一的编码函数 decode() 。

-

除了使用 trait 技术之外,函数重载和模板函数 + 内置字段也可以实现,前者每增加一种数据类型就需要重新实现一个函数,而同一类数据(int, unsinged int)可以使用同样的编码方法。我们想要的是针对同一种数据类型,只编写一个函数。后者对于系统自定义变量 int, double 而言,是无法在其内部定义 type 的。

-

traits 技术的关键在于,使用另外的模板类 type_traits 来保存不同数据类型的 type,这样就可以兼容自定义数据类型和内置数据类型:

-
1
2
3
4
5
6
// 定义数据 type 类
enum Type {
TYPE_1,
TYPE_2,
TYPE_3
}
- -

对于自定义类型,在类内部定义 type,然后在 traits 类中定义同样的 type:

-
1
2
3
4
5
6
7
8
9
10
11
12
13
// 自定义数据类型
class Foo {
public:
Type type = TYPE_1;
};
class Bar {
public:
Type type = TYPE_2;
};
template<typename T>
struct type_traits {
Type type = T::type;
}
- -

对于内置数据类型,使用模板类的特化为自定义类型生成独有的 type_traits:

-
1
2
3
4
5
6
7
8
9
// 内置数据类型
template<typename int>
struct type_traits {
Type type = Type::TYPE_1;
}
template<typename double>
struct type_traits {
Type type = Type::TYPE_3;
}
- -

这样就可以为不同数据类型生成统一的模板函数

-
1
2
3
4
5
6
7
8
9
10
// 统一的编码函数
template<typename T>
void decode<const T& data, char* buf) {
if(type_traits<T>::type == Type::TYPE_1) {
...
}
else if(type_traits<T>::type == Type::TYPE_2) {
...
}
}
- -

总结

-
    -
  • traits 技术的关键在于使用第三方模板类 traits,利用模板特化的功能, 实现对自定义数据和编译器内置数据的统一
  • -
  • 这个例子使用了枚举变量来表示数据类型,而实际操作中通常使用不同的类来表示不同的类型,这样可以在编写模板函数时更好的优化。
  • -
  • tratis 技术常见于标准库的实现中,但对日常开发中降低代码冗余也有很好的借鉴意义
  • -
  • C++20 提供了Concept 的特性,使用Concept 可以使得实现类似的功能更加方便
  • -
- -
- - - - - -
- - - - - - - -
- - -
-
-
- - - -
- - -
-
-
-
-
-
- -
-
-
-
-
-
-
-
-
- - - - - - - - - diff --git "a/2023/05/11/C-vector-\347\232\204-push-back-\345\222\214-emplace-back/index.html" "b/2023/05/11/C-vector-\347\232\204-push-back-\345\222\214-emplace-back/index.html" deleted file mode 100644 index ef87129a..00000000 --- "a/2023/05/11/C-vector-\347\232\204-push-back-\345\222\214-emplace-back/index.html" +++ /dev/null @@ -1,279 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - -C++ vector 的 push_back 和 emplace_back | 暮秋小屋 - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
-
- -
- -
-
-
- - - -
-
-
- - -
-
-
- - -
- -
- -
- -
-
-
-
1
2
3
4
5
6
/// Inserts a new element at the end of the vector, right after its current last element. This new element is constructed in place using args as the arguments for its constructor.
/// This effectively increases the container size by one, which causes an automatic reallocation of the allocated storage space if -and only if- the new vector size surpasses the current vector capacity.
/// The element is constructed in-place by calling allocator_traits::construct with args forwarded.
///A similar member function exists, push_back, which either copies or moves an existing object into the container.
template <class... Args>
void emplace_back (Args&&... args);
- -

push_back 会构造一个临时对象,这个临时对象会被拷贝或者移入到容器中,然而 emplace_back 会直接根据传入的参数在容器的适当位置进行构造而避免拷贝或者移动。

-

传统观点认为 push_back 会构造一个临时对象,这个临时对象会被移入到 v 中,然而 emplace_back 会直接根据传入的参数在适当位置进行构造而避免拷贝或者移动。从标准库代码的实现角度来说这是对的,但是对于提供了优化的编译器来讲,上面示例中最后两行表达式生成的代码其实没有区别。

-

真正的区别在于,emplace_back 更加强大,它可以调用任何类型的(只要存在)构造函数。而 push_back 会更加严谨,它只调用隐式构造函数。隐式构造函数被认为是安全的。如果能够通过对象 T 隐式构造对象 U,就认为 U 能够完整包含 T 的所有内容,这样将 T 传递给 U 通常是安全的。正确使用隐式构造的例子是用 std::uint32_t 对象构造 std::uint64_t 对象,错误使用隐式构造的例子是用 double 构造 std::uint8_t。

-

如果想要调用显示构造函数,那么就调用 emplace_back。如果只希望调用隐式构造函数,那么请使用更加安全的 push_back:

-
1
2
3
4
std::vector<std::unique_ptr<T>> v;
T a;
v.emplace_back(std::addressof(a)); // compiles
v.push_back(std::addressof(a)); // fails to compile
- -

std::unique_ptr<T> 包含了显示构造函数通过 T* 进行构造。因为 emplace_back 能够调用显示构造函数,所以传递一个裸指针并不会产生编译错误。然而,当 v 超出了作用域,std::unique_ptr<T> 的析构函数会尝试 delete 类型 T* 的指针,而类型 T* 的指针并不是通过 new 来分配的,因为它保存的是栈对象的地址,因此 delete 行为是未定义的。

- -
- - - - - -
- - - - - - - -
- - -
-
-
- - - -
- - -
-
-
-
-
-
- -
-
-
-
-
-
-
-
-
- - - - - - - - - diff --git "a/2023/05/12/C-20-\345\256\236\347\216\260-string-split/index.html" "b/2023/05/12/C-20-\345\256\236\347\216\260-string-split/index.html" deleted file mode 100644 index a2db0de9..00000000 --- "a/2023/05/12/C-20-\345\256\236\347\216\260-string-split/index.html" +++ /dev/null @@ -1,274 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - -C++ 20 实现 string split | 暮秋小屋 - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
-
- -
- -
-
-
- - - -
-
-
- - -
-
-
- - -
- -
- -
- -
-
-
-

C++20引入了范围库ranges,其中提供的两个范围适配器std::split、std::lazy_split可以使我们以一种更为优雅的形式实现split:

-
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
#include <concept>
#include <ranges>
#include <algorithm>
#include <format>
#include <iostream>

#define stdr std::ranges
#define stdrv std::ranges::views

template<template<typename> typename Container = std::vector, typename Arg = std::string_view>
auto Split(std::string_view str, std::string_view delimiter)
{
Container<Arg> myCont;
auto temp = str
| stdrv::split(delimiter)
| stdrv::transform([](auto&& r)
{
return Arg(std::addressof(*r.begin()), stdr::distance(r));
});
auto iter = std::inserter(myCont, myCont.end());
stdr::for_each(temp, [&](auto&& x) { iter = {x.begin(), x.end()}; });
return myCont;
}
int main()
{
std::string str = "Hello233C++20233and233New233Spilt";
std::string delimiter = "233";
auto&& strCont = Split<std::list, std::string>(str, delimiter);
stdr::for_each(strCont, [](auto&& x) { std::cout << std::format("{} ", x); });
}
//output: Hello C++20 and New Spilt
- -

C++20没有提供关键的 ranges::to<container>函数,导致demo中还需要额外封装并手写for_each来写入数据,等到C++23实装了该函数,split的实现会比现在简洁优雅的多,真正做到方便泛用、无需封装:

-
1
2
3
auto&& strCont = str
| stdrv::lazy_split(delimiter)
| stdr ::to<std::vector<std::string>>;
-
- - - - - -
- - - - - - - -
- - -
-
-
- - - -
- - -
-
-
-
-
-
- -
-
-
-
-
-
-
-
-
- - - - - - - - - diff --git "a/2023/05/24/Rust-\351\227\255\345\214\205-lifetime-may-not-live-long-enough-\351\227\256\351\242\230/index.html" "b/2023/05/24/Rust-\351\227\255\345\214\205-lifetime-may-not-live-long-enough-\351\227\256\351\242\230/index.html" index 3e34ba99..ff0dfa44 100644 --- "a/2023/05/24/Rust-\351\227\255\345\214\205-lifetime-may-not-live-long-enough-\351\227\256\351\242\230/index.html" +++ "b/2023/05/24/Rust-\351\227\255\345\214\205-lifetime-may-not-live-long-enough-\351\227\256\351\242\230/index.html" @@ -202,7 +202,7 @@
- Next + Next
diff --git "a/2023/06/10/\344\272\214\351\233\266\344\272\214\344\270\211\345\271\264\345\205\255\346\234\210\345\215\201\346\227\245/index.html" "b/2023/06/10/\344\272\214\351\233\266\344\272\214\344\270\211\345\271\264\345\205\255\346\234\210\345\215\201\346\227\245/index.html" index 4ebbe5eb..8cc6e28e 100644 --- "a/2023/06/10/\344\272\214\351\233\266\344\272\214\344\270\211\345\271\264\345\205\255\346\234\210\345\215\201\346\227\245/index.html" +++ "b/2023/06/10/\344\272\214\351\233\266\344\272\214\344\270\211\345\271\264\345\205\255\346\234\210\345\215\201\346\227\245/index.html" @@ -158,11 +158,11 @@
-

我希望与我一样独自生活多年的宝宝们,能绝对理性的面对所有事情,这是一个人工作学习生活的基础,但同时要感同身受的去阅读文学类作品,这是让我们保留一丝感性的方式,感性是不可或缺的,理性可以让人控制感性,但也可以消灭它。

-

一个人的世界很容易出现信息茧房,要不断的和人交流,把很多想法说出来,接收一切评论,不然会在长期的不分正确错误的信息堆叠中出现一团巨大的闭塞性的知识,这很不利于我们快乐的活下去。

-

接收到的评论不必急于改变,先存起来,让它们陪着你跟着时间走,路上会慢慢的和其他的事情连结起来,这样就能择其善者而从之,其不善者而改之了。

-

和我一样处在青少年阶段的宝宝们应当早日从这喧扰的世界冷静下来,让脑子里满是憧憬和情爱的灵魂得到一丝陈酿,理性点抬头看看世界上方的二氧化碳,自己晃晃头打破能回到最初的样子再重来的梦。

-

爱你们噢。

+

请绝对理性的面对所有事情,这是一个人工作学习生活的基础,但同时要感同身受的去阅读文学类作品,这是保留一丝感性的方式,感性是不可或缺的,理性可以让人控制感性,但也可以消灭它。

+

独处的世界很容易出现信息茧房,要不断的和人交流,把很多想法说出来,接收一切评论,不然会在长期的不分正确错误的信息堆叠中出现一团巨大的闭塞性的知识,这很不利于快乐的活下去。

+

接收到的评论不必急于改变,先存起来,让它们陪着自己,跟着时间走,路上会慢慢的和其他的事情连结起来,这样就能择其善者而从之,其不善者而改之了。

+

应当早日从这喧扰的世界冷静下来,让脑子里满是憧憬和情爱的灵魂得到一丝陈酿,理性点抬头看看世界上方的二氧化碳,自己晃晃头打破能回到最初的样子再重来的梦。

+

爱你们。

-- cgit v1.2.3