Upgrade to Pro
— share decks privately, control downloads, hide ads and more …
Speaker Deck
Sign up for free
Menu
Search
Features
All features
Private URLs
Password Protection
Custom URLS
Scheduled publishing
Remove Branding
Restrict embedding
Deck Collections
Notes
Features
All features
Private URLs
Password Protection
Custom URLS
Scheduled publishing
Remove Branding
Restrict embedding
Deck Collections
Notes
Explore
Featured decks
Featured speakers
Programming
Technology
Storyboards
Explore
Featured decks
Featured speakers
Programming
Technology
Storyboards
Pricing
Search
Sign in
Sign up for free
非类型参数会梦见 OOP 吗?
Search
Zhihao Yuan
January 27, 2023
Programming
200
0
Share
Embed
Copy iframe code
Copy JS code
Copy link
Start on current slide
非类型参数会梦见 OOP 吗?
非类型模板参数对多态和参量化类型的启示。
视频:
https://www.bilibili.com/video/BV1bK411i7wL
Zhihao Yuan
January 27, 2023
More Decks by Zhihao Yuan
See All by Zhihao Yuan
DWARF 眼中的 C++
lichray
0
75
Callable Objects in Java, C#, Rust, and C++
lichray
0
81
动态库,是得多动态?
lichray
0
140
Dynamically Loaded Libraries Outside the Standard
lichray
0
500
Thinking in Immediate: ImGUI
lichray
0
270
Lambda 和它们的环境
lichray
1
270
The Many Shades of reference_wrapper
lichray
0
260
Are We Macro-free Yet?
lichray
0
190
vvpkg: A cross-platform data deduplication library in C++14
lichray
0
210
Other Decks in Programming
See All in Programming
Apple Intelligenceとオンデバイス時代の到来
yahir0
0
230
AIがコードを書いてくれる時代、我々は何を確かめるのか
yosuke_furukawa
PRO
13
5.8k
クラウドエージェントは、 何がそんなに便利なのか / what makes cloud agents so useful
rkaga
5
810
Secure Your Coding Agent Like It’s Malware (v1.5) 🇬🇧 @Code.talks 2026
richargh
0
110
DroidKnights 2026 Android A2UI Jetpack Compose 기반의 안전한 AI UI 렌더링 - 박제창
itsmedreamwalker
1
210
3回つくってわかった、 定期実行ジョブ基盤の本質情報
takai
4
3.8k
當我們開始不讀 Code - AI Agent 時代,Code Review 與 Clean Code 還剩下什麼?
jame2408
1
370
AIが生成したコードは読むべきか?抽象化設計とコードの品質保証 / abstraction-design-and-quality
minodriven
16
8.9k
FactoryBotをやめればRSpecは速くなるのか?
yuta_onishi_97
0
1.6k
学びを記事・スライド・Xへ高速展開する― AI-assisted Learning Loopで回すコンテンツ生成術
ochtum
0
200
Next.js: The final boss of Web Performance
guaca
0
390
Railsバージョンを4倍にした話
optim
0
970
Featured
See All Featured
Hiding What from Whom? A Critical Review of the History of Programming languages for Music
tomoyanonymous
3
1.4k
Accessibility Awareness
sabderemane
1
290
Producing Creativity
orderedlist
PRO
348
41k
A Soul's Torment
seathinner
8
3.9k
Rebuilding a faster, lazier Slack
samanthasiow
85
9.7k
Why Your Marketing Sucks and What You Can Do About It - Sophie Logan
marketingsoph
0
470
The browser strikes back
jonoalderson
0
1.9k
The agentic SEO stack - context over prompts
schlessera
0
1k
Conquering PDFs: document understanding beyond plain text
inesmontani
PRO
4
3.3k
Build The Right Thing And Hit Your Dates
maggiecrowley
39
3.5k
Dominate Local Search Results - an insider guide to GBP, reviews, and Local SEO
greggifford
PRO
0
400
Reality Check: Gamification 10 Years Later
codingconduct
0
2.4k
Transcript
非类型参数会梦见 OOP 吗? Zhihao Yuan <
[email protected]
>, 22-Oct-22
• 非类型(模版)参数 • 给 auto 模版参数传函数名 • nontype_t<f> • 原理
& 多态 3
Part 1 背景知识 4
非类型参数 (pre-C++14) • template<class T, size_t N> struct array; •
template<class T, T v> struct integral_constant; 5 有具体类型的非类型参数 类型为另一类型参数 的非类型参数
auto 非类型参数 (C++14) • template<auto v> struct constant; • constant<3>
→ decltype(v) is int • constant<'a'> → decltype(v) is char 6 同时具有值依赖和 类型依赖 constant<97>
• template<class T, T v> struct integral_constant; • integral_constant<std::nullptr_t, nullptr>
合法吗? 7
#include <concepts> • template<std::integral T, T v> struct integral_constant; •
integral_constant<std::nullptr_t, nullptr> → error: constraints not satisfied for class template 'integral_constant' [with T = std::nullptr_t, v = nullptr] 8
• template<auto v> struct constant; 9
Constrained auto 参数 (C++20) • template<std::integral auto v> struct constant;
10
模版参数与函数参数的互换性 (1/2) • foo<3>() • template<auto v> void foo(); •
template<std::integral auto v> void foo(); 11 • foo(3) • void foo(auto v); • void foo(std::integral auto v);
哪些值可以用非类型参数传递? • 3 • 3.14 • T* and T& •
&Class::member • enum • 用户定义类型? 12 C++20
Structural type (C++20) struct point { double x, y; };
13 基类全为 public structural 类型 成员全为 public structural 类型或 structural 类型的数组
指定初始化 structural 类型的对象 • template<point> void bar(); • bar<{.x =
3.0, .y = 5.0}>() 14
模版参数与函数参数的互换性 (2/2) • foo<3>() • template<auto v> void foo(); •
template<std::integral auto v> void foo(); • bar<{.x = 5.0}>() • template<point> void bar(); 15 • foo(3) • void foo(auto v); • void foo(std::integral auto v); • bar({.x = 5.0}) • void bar(point);
T& 非类型参数的用途 • 具有静态存储类的对象的地址是常量 16
auto& 可以修改模板实参 template<auto& r> void modify() { r = 3;
} 17 int main() { static int x; printf("x = %d\n", x); // x = 0 modify<x>(); printf("x = %d\n", x); // x = 3 }
T& 非类型参数的用途 • auto& 模版参数用于修改具有静态存储类的对象 • 具有静态存储类的对象的地址是常量 • 相比 auto,
auto const& 可传递非 structural 类型的对象 • 对象类型可能不是 literal type 18
Part 2 函数名作为非类型参数的实参 19
高阶函数 in C typedef int pivot_rule(vec); void algorithm_loop(tableau tab, pivot_rule
select_pivot); int dantzig(vec A) { /* ... */ } algorithm_loop(tab, dantzig); 20 使用
高阶函数实现为模版特化 typedef int pivot_rule(vec); template<pivot_rule select_pivot> void algorithm_loop(tableau tab); int
dantzig(vec A) { /* ... */ } algorithm_loop<dantzig>(tab); 21 使用
非类型参数传递函数对象的特点 1. 同一算法对同一类型的函数依然可以产生不同特化 22
函数指针模版参数的灵活性 extern int dantzig(vec A); // 在其它文件中定义 algorithm_loop<dantzig>(tab); 23
非类型参数传递函数对象的特点 1. 同一算法对同一类型的函数指针依然可以产生不同特化 2. 和 lambda 表达式不同,是否内联回调函数是可选的 24
如果不是函数指针呢? template<pivot_rule select_pivot> void algorithm_loop(tableau tab); struct vec { int
find_maximum() const; /* ... */ }; 25 algorithm_loop<&vec::find_maximum>(tab) 可以吗?
同时支持函数指针和成员函数指针 template<auto select_pivot> void algorithm_loop(tableau tab); 26
任意符合签名的函数对象 template<class Fn, class R, class... T> concept invocable_r =
std::is_invocable_r_v<R, Fn, T...>; template<invocable_r<int, vec> auto select_pivot> void algorithm_loop(tableau tab); 27
非类型参数传递无捕捉的闭包 template<invocable_r<int, vec> auto select_pivot> void algorithm_loop(tableau tab); algorithm_loop<[](vec) {
return 0; }>(tab); 28
非类型参数传递函数对象的特点 1. 同一算法对同一类型的函数指针依然可以产生不同特化 2. 和 lambda 表达式不同,是否内联回调函数是可选的 3. auto 可接受不同类型的
callable 对象,包括模板实参列表里的 lambda 29
Part 3 如果没有 operator() 30
First-class 函数对象做参数 • 类型依赖+值依赖 template<auto fn> void algo(); • 类型依赖
void algo(auto fn); • 类型擦除 void algo(std::function<R(Args...)> fn); 31
旧式代码 struct IDoWorkCallback { virtual void OnEvent(WorkResult status, IData& object)
= 0; }; using IDoWorkCallbackPtr = std::shared_ptr<IDoWorkCallback>; struct WorkContext { void Add(IDoWorkCallbackPtr callback); }; 32
现代 C++ struct WorkContext { typedef void OnEvent(WorkResult status, IData&
object); void Add(std::function<OnEvent> callback); }; 33
现有代码 struct CMyWorkCallback : IDoWorkCallback { void OnEvent(WorkResult status, IData&
object) override { /* ... */ } }; ctx.Add(std::make_shared<CMyWorkCallback>()); 34 不是 std::function 也不是可调用的对象
一种迁移方法:增加重载 struct WorkContext { typedef void OnEvent(WorkResult status, IData& object);
void Add(std::function<OnEvent> callback); void Add(IDoWorkCallbackPtr callback) { Add([=](WorkResult status, IData& object) { callback->OnEvent(status, object); }); } }; 35
能避免引入额外对象吗? struct WorkContext { typedef void OnEvent(WorkResult status, IData& object);
void Add(std::function<OnEvent> callback); void Add(IDoWorkCallbackPtr callback) { Add(std::bind_front(&IDoWorkCallback::OnEvent, callback)); } }; 36 C++20
用非类型参数传递信息 struct WorkContext { typedef void OnEvent(WorkResult status, IData& object);
void Add(std::function<OnEvent> callback); void Add(IDoWorkCallbackPtr callback) { Add({ nontype<&IDoWorkCallback::OnEvent>, callback }); } }; 37
P2511 struct WorkContext { typedef void OnEvent(WorkResult status, IData& object);
void Add(std::function<OnEvent> callback); void Add(IDoWorkCallbackPtr callback) { Add({ nontype<&IDoWorkCallback::OnEvent>, callback }); } }; 38
std::nontype template<auto> struct nontype_t { explicit nontype_t() = default; };
template<auto f> inline constexpr nontype_t<f> nontype; 39
使用新 API 的过渡代码 struct CMyReportingCallback : IDoWorkCallback { void OnEvent(WorkResult
status, IData& object) override; }; CMyReportingCallback cb; ctx.Add({ nontype<&CMyReportingCallback::OnEvent>, cb }); 40
无需覆写具体的成员函数 struct CMyReportingCallback : IDoWorkCallback { void OnEvent(WorkResult status, IData&
object) override; }; CMyReportingCallback cb; ctx.Add({ nontype<&CMyReportingCallback::OnEvent>, cb }); 41 Notify Notify
两个需求,一个解决方案 • 现有代码接受基于接口的回调 • 没有 operator(), 切换到基于类型擦 除的调用包装器需要某种适配 • 现有代码接受基于类型擦除的调用包
装器 • 调用签名匹配,但并非 operator() 的 成员函数需要某种适配 42 让用户指定某样东西作为他们对象的 operator()
CMyReportingCallback cb; ctx.Add({nontype<&CMyReportingCallback::Notify>, cb}); 43
推广到其它调用包装器 CMyReportingCallback cb; ctx.Add({std::nontype<&CMyReportingCallback::Notify>, cb}); 44 •Add(std::function) •Add(std::move_only_function) •Add(std::function_ref) •Add(std::copyable_function)
C++26?
用无捕捉闭包重写逻辑 CMyReportingCallback cb; ctx.Add({nontype< [](auto& cb, WorkResult status, IData& object)
{ LOG(INFO) << "status: " << status; cb.Notify(status, object); }>, cb}); 45
Part 4 从非类型参数的视角看多态性 46
nontype 改变了什么? • 原本没有 operator() 的类型,现在依然没有 • 原本不满足 invocable<Args...> concept
的类型,现在满足了 47
概念适配 struct CMyReportingCallback { void Notify(WorkResult status, IData& object); };
template<invocable<WorkResult, IData&> T> void Accept(T f); Accept(cb); 48
C++0x Concept struct CMyReportingCallback { void Notify(WorkResult status, IData& object);
}; concept_map invocable<CMyReportingCallback, WorkResult, IData&> { using operator() = CMyReportingCallback::Notify; }; 49
Rust Traits trait Callable<Args> { fn call(&self, args: Args); }
impl Callable<(WorkResult, IData)> for CMyReportingCallback { fn call(&self, (status, object): (WorkResult, IData)) { self.Notify(status, object); } } 50
从类型检查到类型擦除 fn Accept<T>(cb: &T) where T: Callable<(WorkResult, IData)> fn Accept(cb:
&dyn Callable<(WorkResult, IData)>) 51
Trait Object 如何使用两个虚表 • impl 可以扩展已完成的类 • impl 块可以拆分到不同的模块中 •
C++ 继承自多个纯虚基类必须在定义类的同时完成 52 CMyReportingCallback IDoWorkCallback Callable<(WorkResult, IData)>
经典 OOP in C++ class CDraw : public IDraw {
Color color_; public: void draw() override; void set_color(Color) override; ~CDraw() = default; }; 53 class IDraw { public: virtual void draw() = 0; virtual void set_color(Color) = 0; ~IDraw() = default; };
回忆 C++ 对象模型 54 vtable CDraw for IDraw void draw();
void set_color(Color); ~IDraw() vptr … Color color_;
手工构造虚表实现类型擦除 constinit Drawable DrawableImpl = { .draw = [](void* this_)
{}, .set_color = [](void* this_, Color color) {}, .destroy = [](void* this_) {}, }; 55 struct Drawable { typedef void draw_t(void*); typedef void set_color_t(void*, Color); typedef void destroy_t(void*); draw_t* draw; set_color_t* set_color; destroy_t* destroy; };
广义的虚表 56 DrawableImpl 静态对象 void (*draw)(void*); void (*set_color)(void*, Color); void
(*destroy)(void*); ref-wrap<Drawable const> CDraw rep_; ref-wrap<Drawable const> void* obj_; 引用语义 type erasure 或 P0957 “proxy” 值语义 type erasure
Swift Protocol extension CDraw : Drawable { func draw() {}
func set_color(_: Color) {} func close() {} } 57 protocol Drawable { func draw() func set_color(_: Color) func close() }
见证表 (Witness table) • 接口 conformance 的见证 • 可能描述了常量,关联类型等等 •
「类型」被表述为见证表的值 58 impl of CDraw for Drawable func draw(_: Self) func set_color(_: Self, _: Color); func close(_: Self)
抽象出见证表数据结构的作用 func Run<T: Drawable>(fg: T, bg: T) 59 两个实参的实际类型必须相同 同时也是类型擦除
当类型被建模为一个值 • 静态分发 template<auto const& witness> void Run(e<CDraw> fg, e<CDraw>
bg); • e 代表 existential container • 动态分发 void Run(auto const& witness, e<CDraw> fg, e<CDraw> bg); 60 func Run<T: Drawable>(fg: T, bg: T)
泛型函数传参 61 impl of CDraw for Drawable func draw(_: Self)
func set_color(_: Self, _: Color); func close(_: Self) // Small Buffer CDraw storage_; Drawable const&
结语 • OOP、类型擦除、参量化多态,在这两个方面有所不同: • 使用何种 Existential container • 如何引用 Witness
table 62
各种多态方案的实现 63 Existential container Witness table C++ 多态对象 指针 内嵌
vptr Rust trait objects 胖指针,基于不同引用 胖指针之一 Swift generics SBO 通过静态或运行时的参数传 递,保存时使用更大的容器 std::function_ref 胖指针 无,直接指向 thunk std::move_only_function SBO vptr 存在容器内,可修改 pro::proxy from P0957 胖指针,可选 owning 胖指针之一
结语 • OOP、类型擦除和参量化多态在 existential container 和传递 witness table 上有所不同 •
非类型参数可以在编译时携带多样的信息 • 在非类型参数或函数参数之间切换传递 witness table 的思想可以借鉴 64
Questions? 65 @lichray zhihaoy/nontype_functional