据我了解,表达式模板将在 C++11 中基于范围的 for 中断,因为其中for (auto x : expr)
有一个隐含auto&& __range = expr
的含义,这将导致悬空引用。
有没有办法创建表达式模板类,以便它们在基于范围的 for 中正确运行,或者至少引发编译错误?
基本上,我想防止表达式模板正确编译但由于悬空引用而在运行时失败的可能性。我不介意在基于范围的 for 中使用表达式模板之前必须将它们包装在某些东西中,只要在用户忘记包装表达式模板时没有静默运行时错误。
据我了解,表达式模板将在 C++11 中基于范围的 for 中断,因为其中for (auto x : expr)
有一个隐含auto&& __range = expr
的含义,这将导致悬空引用。
有没有办法创建表达式模板类,以便它们在基于范围的 for 中正确运行,或者至少引发编译错误?
基本上,我想防止表达式模板正确编译但由于悬空引用而在运行时失败的可能性。我不介意在基于范围的 for 中使用表达式模板之前必须将它们包装在某些东西中,只要在用户忘记包装表达式模板时没有静默运行时错误。
通常对此您无能为力。如果您将表达式作为范围,它必须解析为在for
语句初始化后有效的内容。并且没有办法在编译时检测到任何特定类型是由auto
.
最好让你的表达系统更加基于移动,这样它就不必保存引用。auto
与尝试存储对潜在死物的引用相比,这将产生更安全的结果。如果不可移动类型的复制给您带来困扰,那就忍受它。
我能想到几个选项,每个选项都有自己的丑陋之处。
一个明显的选择是使用指针(可能unique_ptr
)而不是引用。当然,为了让它工作,它要么需要从堆中分配,要么需要自定义分配器。我认为使用一个好的分配器,这种方法有一些优点。话又说回来,运算符重载只会变得讨厌。
另一种方法是按值而不是通过 const 引用来存储子表达式。这种方法的效率非常依赖于编译器,但是由于您基本上是在处理一堆临时文件,我想现代编译器可以优化掉副本(或者至少是很多副本)。
最后一种方法允许您在代码中保持相同的结构,但会强制用户评估表达式。它要求您只有一种可迭代类型,即表达式的底层类型(例如std::vector<int>
)。任何表达式类都不应该具有为它们定义begin
的end
方法或函数,而应该只是可转换为基础类型。这样,类似代码for(auto x : expr)
将在编译时失败(因为expr
不可迭代),但编写for(auto x : static_cast<vector<int>>(expr))
工作是因为表达式已经被评估。
如果您希望使用基于范围的 for 循环来实现表达式模板操作,那么您可以在表达式模板类中提供私有或受保护的begin
和end
方法。只要确保每个模板类都可以访问其他模板类的begin
和end
方法。在这种情况下应该没问题,因为表达式模板是函数的参数,因此在该函数中编写循环时不必担心悬空引用。