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

CppCon 2014 学习:Wishful Thinking

我们来聊聊名字(names)以及你给的简单结构体示例:

struct sample
{int id;
};

1. 名字(Names)在 C++ 中的作用

  • 名字是程序中识别变量、类型、函数等实体的标识符。
  • 通过名字,编译器和程序员能引用和操作对应的对象或类型。
  • C++ 的名字还涉及作用域(scope)、链接(linkage)、重名(name hiding)、重载等复杂机制。

2. 结构体名字 sample

  • sample 是结构体的类型名。
  • 这个名字定义了一个新的类型,可以用来声明变量,例如:
sample s1;  // 声明一个 sample 类型的变量 s1
s1.id = 42;

3. 结构体成员名 id

  • id 是结构体的成员名,是 sample 类型变量的一个字段。
  • 通过变量名加.操作符访问,比如 s1.id

4. 名字与作用域

  • 结构体名 sample 是在全局作用域定义的,意味着它在定义后整个文件及其它引用了该头文件的代码中都可见。
  • 结构体成员名 id 只在 sample 类型的作用域内有效。

5. 小结

  • 结构体名为类型命名,方便创建该类型变量。
  • 成员名定义该类型内数据的访问和操作。
  • 名字的规则和作用域影响程序如何识别和链接符号。

这段代码是一个模板结构体的示例:

template<typename T>
struct sample
{T id;
};
using my_sample = sample<int>;

解析

1. 模板结构体 sample<T>

  • 这是一个类模板,参数化了成员变量的类型 T
  • T 是模板参数,占位符类型,实际使用时会被具体类型替代。
  • 例如,sample<int> 表示 idint 类型,sample<double> 表示 iddouble 类型。

2. using my_sample = sample<int>;

  • 这是一个类型别名定义(C++11 新特性)。
  • 它定义了 my_sample 等价于 sample<int>
  • 方便代码中直接用 my_sample 代替 sample<int>,简洁明了。

3. 使用示例

my_sample s;    // s 是 sample<int> 类型的变量
s.id = 100;    // id 是 int 类型,可以赋整数值

4. 作用

  • 模板提供了灵活的泛型编程能力,结构体能支持多种数据类型而不用重复写代码。
  • 类型别名提升代码可读性,方便维护和修改。

例子:

template<typename T, name X>
struct sample
{T X;
};
using my_sample = sample<int, id>;

其实在标准 C++ 中是不合法的,因为:

1. C++ 中模板参数的限制

  • 模板参数只能是以下几种:
    • 类型参数typename Tclass T
    • 非类型参数:整型、指针、引用、枚举等有限几种具体值,比如 int Nchar Cconst char* p(有条件)
    • 模板模板参数:模板本身作为参数
  • 不能用名字(标识符)作为模板参数,也不能把成员变量名作为模板参数。

2. 你想表达的“Name Literal”

你看起来想表达“用名字作为模板参数”,让成员变量的名字变成模板参数,这种功能目前 C++ 不支持。

3. 目前能做什么?

  • 你可以用非类型模板参数传递常量值(整型、指针等)。
  • 不能用“名字”做模板参数。
  • 如果想让类成员名可变,可以考虑宏或者代码生成,但不是通过模板参数。

4. 例如

template<typename T, int N>
struct sample
{T data[N];  // 使用 N 作为大小
};

这在 C++ 是允许的,但这里的非类型参数 N 是值,不是名字。

5. 未来的 C++?

  • 有提案讨论“字符串字面量作为非类型模板参数”的支持,比如:
template <typename T, auto Name>
struct sample
{T id;
};
  • 其中 Name 可以是字符串字面量,间接实现名字传递,但这还不是标准。

总结

  • 标准 C++ 不能用名字(标识符)做模板参数。
  • “名字字面量”作为模板参数是目前的幻想(wishful thinking)。
  • 可以用值(int、指针等)作为非类型模板参数。
  • 需要名字灵活变动只能靠宏或代码生成。

这个例子:

template<typename... T>
struct named_tuple
{T::member_type T::member_name...;
};

在标准 C++ 里是不能通过的。

为什么?

1. 模板参数包 typename... T

  • T 是类型参数包,代表多个类型。
  • 你想在结构体内展开成员声明:
T::member_type T::member_name...;
  • 但 C++ 不支持用类型成员名(member_name)直接作为成员变量名展开。
  • T::member_type 这个写法意味着每个类型 T 有一个名为 member_type 的类型成员,这可以,但你不能用 T::member_name 动态生成成员变量名。

2. C++ 不支持成员名作为模板参数展开

  • 模板只能参数化类型或非类型的值(数字、指针等),不能参数化“成员变量名”
  • 所以无法写出“根据类型内定义的成员名,自动声明变量”的代码。

3. 可行的替代方案

  • 使用 std::tuple 搭配辅助的结构或映射,在代码中维护名字和值的对应关系(比如 std::pair<name, value>
  • 使用宏或者代码生成来产生带名字的成员
  • C++23 的 std::tuple 扩展和反射特性,但当前还不支持成员名编程

举个可行的接近实现的例子

template<typename... Ts>
struct named_tuple
{std::tuple<Ts...> data;
};
  • 然后通过外部映射或函数访问对应的成员,名字由用户维护。

总结

  • C++ 不支持直接用类型成员名作为成员变量名展开。
  • 你想的“named members in tuple by template parameter”是语言未来想要的功能,但现在还没支持。
  • 需要名字+值绑定只能借助外部工具、元编程库或者代码生成。

“names”和CRTP(Curiously Recurring Template Pattern,奇异递归模板模式),还提到了 sqlpp11 这个库。

1. Names & sqlpp11

  • sqlpp11 是一个 C++ 的 SQL 类型安全库,广泛利用了模板和命名技术,把 SQL 语句和字段名映射成 C++ 类型系统中的名字和表达式。
  • 里面大量使用模板元编程和编译时名字表达,解决了“如何用类型安全和名字表示 SQL 字段和查询”的实际问题。
  • 这是“names”在现实世界的典型应用示例。

2. CRTP 简单介绍

你给的代码示例:

template<typename Derived>
struct base
{static_assert(Derived::value, "ooops");decltype(auto) get() const{return static_cast<const Derived&>(*this);}
};
struct composite : public base<composite>
{static constexpr bool value = true;
};

CRTP(奇异递归模板模式)是什么?

  • 是一种模板设计模式,基类模板参数传入派生类自身类型。
  • 这样基类可以访问派生类的静态成员、类型等。
  • 常用于静态多态、编译期接口检测、策略类设计。

代码解析

  • base 是模板基类,接受派生类 Derived 作为模板参数。
  • static_assert(Derived::value, "ooops");:编译时检查派生类是否定义了静态常量成员 value,并且为 true
  • get() 方法安全地把基类指针转换成派生类引用,方便访问派生类成员。
  • composite 继承自 base<composite>,满足 CRTP 规则。
  • 它定义了 static constexpr bool value = true;,通过 static_assert 检查。

CRTP 的好处

  • 编译时接口检查:确保派生类实现了某些成员。
  • 静态多态:比虚函数开销低,避免运行时开销。
  • 复用代码:基类可以调用派生类的方法和数据。

3. 关联回 “names”

  • CRTP 允许基类在编译期访问派生类的名字(类型名、成员名、静态成员等)。
  • 这是 C++ 模板元编程强大的地方。
  • sqlpp11 也利用类似技巧,把 SQL 查询中的字段名映射成类型和静态成员,保证类型安全和名字匹配。

总结

主题说明
sqlpp11真实世界用例,利用名字和模板元编程实现类型安全SQL
CRTP模板基类用派生类名作参数,支持静态多态和接口检查
关联CRTP通过名字绑定实现编译时访问派生类成员

这段代码:

mixin base
{static_assert(value, "ooops");decltype(auto) get() const{ return *this; }
};
struct composite
{static constexpr bool value = true;using mixin base;
};

——这在标准 C++ 里是不合法的,因为:

mixin 不懂什么意思干嘛的

1. mixin 关键字不存在

  • C++ 里没有 mixin 这个关键字,也没有 using mixin base; 这种语法。
  • 你这是“wishful thinking”(理想中的语法糖),表达想要方便地把一个类“混入”另一个类。

2. 目前 C++ 如何实现 mixins?

常用的方法是继承(通常是模板继承),比如:

template<typename Derived>
struct base
{static_assert(Derived::value, "ooops");decltype(auto) get() const{ return static_cast<const Derived&>(*this); }
};
struct composite : base<composite>
{static constexpr bool value = true;
};
  • 这是 CRTP 模式,允许 base 访问 composite 的成员。

3. using 的作用

  • using 在 C++ 里一般用于引入基类成员名(方法或类型别名)到派生类作用域。
  • 但不能用 using mixin base; 这样写来混入类型。

4. 如果想写类似 mixin 语法糖,怎么做?

  • 目前只能通过或者代码生成工具模拟。
  • 未来 C++ 可能引入概念和 mixin 支持(但还没到那个阶段)。

总结

你想要的语法标准C++做法
mixin base { ... }template<typename D> struct base { ... };
using mixin base;struct composite : base<composite> { ... };

C++ 中的 mixin 是什么

什么是 Mixin?

Mixin 是一种编程技术,意思是“混入”,指一个类(或模板)提供某些功能,可以被其他类继承,从而给其他类“混入”这部分功能。
它不是完整的“基类”,也不是独立对象,而是一种模块化的代码复用方式。

为什么用 Mixin?

  • 代码复用:不用复制粘贴代码,把公共功能写在 mixin 里,多类共享。
  • 组合功能:可以组合多个 mixin,实现复杂行为。
  • 避免菱形继承问题:相比传统多继承,mixin 更轻量。

C++ 中 Mixin 的典型实现

1. 通过模板实现 Mixin(最常见)

template<typename Derived>
struct LoggerMixin
{void log(const std::string& msg) const{std::cout << "Log: " << msg << std::endl;}void debug() const{static_cast<const Derived*>(this)->doDebug();}
};
struct MyClass : LoggerMixin<MyClass>
{void doDebug() const{std::cout << "Debugging MyClass" << std::endl;}
};
int main()
{MyClass obj;obj.log("Hello, Mixin!");obj.debug();
}
  • 这里的 LoggerMixin 是一个模板 mixin,利用 CRTP(Curiously Recurring Template Pattern)访问派生类功能。
  • MyClass 继承 LoggerMixin<MyClass>,获得了 log() 方法。

2. 通过普通多继承实现 Mixin

struct LoggerMixin
{void log(const std::string& msg) const{std::cout << "Log: " << msg << std::endl;}
};
struct MyClass : LoggerMixin
{// 自己的功能
};
  • 更简单直接,但缺少 CRTP 的灵活性。

Mixin 的优点

  • 灵活组合:可以继承多个 mixin,组合各种功能。
  • 避免代码重复:不用复制功能代码。
  • 静态多态:用 CRTP 还能在编译期绑定,提高性能。

什么时候用 Mixin?

  • 当多个类共享某些功能代码时。
  • 想用组合而非深层继承。
  • 需要在编译期检查接口(用 CRTP)。

总结

概念说明
Mixin一种代码复用技术,通过类继承“混入”功能
C++ 实现多继承或者模板(CRTP)实现的类模板
优点代码复用、组合灵活、静态多态

Mixin 的本质和它与继承的关系

Mixin 是什么?

  • Mixin 是一种设计思想,表示“把一段功能代码模块化成一个类或组件,可以在多个类里复用”。
  • 这段功能代码通常以类的形式存在,但它并不一定是传统意义上的“基类”。
  • 它强调的是功能的复用和组合,而不是“类型的继承层次”。

Mixin 与继承的关系

  • 实现方式:C++里实现 mixin 通常用继承(尤其是模板继承CRTP),这是一种技术手段,不是本质定义。
  • 区别于普通基类继承
    • 普通继承往往强调“是一个”(is-a)关系,比如 Cat : Animal,猫是动物。
    • Mixin 更多是“把功能混入”(mix-in),强调“具有某种功能”,不是类型继承的主线。
  • 所以:Mixin 用继承来实现功能组合,但语义上不是传统的继承关系。

举个比喻

  • 继承是“物种分类”:猫继承自动物,鸟继承自动物。
  • Mixin 是“给动物装上GPS模块”——给类额外的功能,不影响主类型身份。

C++中 Mixin 的实现典型例子

// Mixin类,只是功能的集合
template<typename T>
struct PrintableMixin
{void print() const {static_cast<const T&>(*this).print_impl();}
};
// 具体类,混入功能
struct MyClass : PrintableMixin<MyClass>
{void print_impl() const {std::cout << "MyClass printing" << std::endl;}
};
  • PrintableMixin 并不是一个“主基类”,只是功能模块。
  • MyClass 继承它来获得 print() 功能。
  • 这里的“继承”是为了实现 mixin,但功能本身是“组合”,不是传统“是一个”的继承。

结论

观点解释
Mixin 是继承吗?实现时通常用继承,但语义是功能混入,不是类型继承
Mixin 的本质是什么?模块化功能代码,组合进类中实现复用
继承 vs Mixin继承强调类型关系,Mixin强调功能组合
http://www.xdnf.cn/news/788113.html

相关文章:

  • Gitee Wiki:重塑关键领域软件研发的知识管理范式
  • Android Kotlin 算法详解:链表相关
  • 关于线缆行业设备数据采集异构问题的解决
  • D2-基于本地Ollama模型的多轮问答系统
  • [蓝桥杯]最大化股票交易的利润
  • HarmonyOS图片image使用
  • linux的实时性
  • python第31天打卡
  • Python 接口:从协议到抽象基 类(使用猴子补丁在运行时实现协议)
  • LangChain实战:文档加载、分割与向量存储详解
  • 第三十三天打卡复习
  • 机器学习——放回抽样
  • 008房屋租赁系统技术揭秘:构建智能租赁服务生态
  • Matlab自学笔记五十七:符号运算、可变精度运算、双精度浮点型运算,三种运算精度的概念、比较、选择和应用
  • 【25-cv-05991】Keith律所代理Ana Maria油画版权
  • kubeSphere安装使用
  • 流、线程、任务、队列等相关在不同语言的区别联系
  • 项目计划缺乏风险评估和应对策略,如何完善
  • Qiskit:量子计算模拟器
  • Prj09--8088单板机C语言8253产生1KHz方波(1)
  • HTTP Error 400 Bad request 问题分析解决
  • spring boot应答500问题跟踪
  • YAML 文件中不同格式的含义详解
  • Flink 重启后事件被重复消费的原因与解决方案
  • Deep Search之R1-Searcher系列
  • QT实现动画翻转效果
  • Docker 镜像深度剖析:构建、管理与优化
  • 多模态知识图谱可视化构建(neo4j+python+flask+vue环境搭建与示例)
  • 秋招准备-数据结构
  • 前端面试题之Class详解