表达式模板和基于C++11中的范围

Expression templates and ranged based for in C++11

本文关键字:C++11 范围 表达式      更新时间:2023-10-16

我的理解是,表达式模板在C++11中会破坏基于ranged的for,因为for (auto x : expr)中有一个隐式auto&& __range = expr,这将导致悬空引用。

有没有一种方法可以创建表达式模板类,使它们在基于范围的for中正确运行,或者至少引发编译错误?

基本上,我想防止表达式模板正确编译但在运行时由于悬挂引用而失败的可能性。我不介意在基于范围的for中使用表达式模板之前必须将其包装在某种东西中,只要用户忘记包装表达式模板时不会出现静默运行时错误。

通常对此无能为力。如果给定一个表达式作为范围,则它必须解析为for语句初始化后有效的值。并且在编译时无法检测到任何特定类型是由auto推导出来的。

最好使您的表达式系统更加基于移动,这样它就不必包含引用。与尝试存储对潜在死东西的引用相比,auto将产生更安全的结果。如果不可移动类型的复制让你感到困扰,那就接受它吧。

我能想到几个选项,每个选项都有自己的丑陋之处。

一个明显的选择是使用指针(可能是unique_ptr)而不是引用。当然,为了实现这一点,它要么需要来自堆的分配,要么需要自定义分配器。我认为,对于一个好的分配器,这种方法有一些优点。再说一遍,操作员超载只会变得很糟糕。

另一种方法是按值存储子表达式,而不是按常量引用存储。这种方法的效率非常依赖于编译器,但由于你基本上要处理一堆临时文件,我想现代编译器可以优化掉副本(或者至少是很多副本)。

最后一种方法允许您对代码保持相同的结构,但强制用户计算表达式。它要求您只有一个可迭代类型,即表达式的底层类型(比如std::vector<int>)。任何表达式类都不应该为它们定义beginend方法或函数,而应该只转换为基础类型。这样,像for(auto x : expr)这样的代码在编译时会失败(因为expr是不可迭代的),但编写for(auto x : static_cast<vector<int>>(expr))是有效的,因为表达式已经求值了。

如果希望使用基于范围的for循环来实现表达式模板操作,那么可以在表达式模板类中提供私有或受保护的beginend方法。只需确保每个模板类都可以访问其他模板类的beginend方法。在这种情况下应该是可以的,因为表达式模板是函数的参数,所以在该函数中编写循环时不必担心悬挂引用。