c++ - 使用表达式:如何最小化运行时构建时间
问题描述
我有两个类,一个表达式 ( SE
) 和一组两个表达式 ( ME
)。bundle 本身就是一个表达式,因此它可以是另一个 bundle 的一个元素。
struct SE {
SE(char id, char n) : id(id), n(n) {}
size_t size() const { return n; }
char *eval(char *b) const { b[0]=id; return b+1; }
char id, n;
};
template <typename LHS>
struct ME {
ME(const LHS& l, const SE& r) : lhs(l), rhs(r) { }
size_t size() const { return rhs.size(); }
char *eval(char *b) const { *b++='('; b=lhs.eval(b); *b++=','; b=rhs.eval(b); *b++=')'; return b; }
LHS lhs;
SE rhs;
};
捆绑包的构造基于数据成员执行简单的有效性检查n
,可ME
通过方法访问size
。eval
方法使用数据成员进行一些计算id
。在编译时既不知道n
也不知道。id
对于这两个类,我重写了逗号运算符,以便它将多个单个表达式递归捆绑到一个嵌套包中。
auto SE::operator,(const SE& r) { return ME<SE>(*this, r); }
auto ME<LHS>::operator,(const SE& r) { return ME<ME<LHS>>(*this, r); }
我希望,在构建整个包之后,在整个包上eval
触发该方法。例子:
SE('a',1); // prints 'a'
SE('a',1), SE('b',1); // prints '(a,b)'
SE('a',1), SE('b',1), SE('c',1); // prints '((a,b),c)'
实现这一点的一种可能方法是使用类的析构函数并添加一个标志,该标志在andis_outer
的构造期间适当更新。当这些类中的任何一个被破坏时,如果标志表明这是最外层的类,则被触发。下面给出了一个完整的演示。SE
ME
eval
在godbolt上测试下面的简单demo
函数,在我看来,编译器生成的代码比严格必要的要多。虽然id
和n
在编译时不知道,但表达式的最终类型应该是。我希望捆绑包的整个构造减少到仅将几个数字移动到正确的位置,然后检查断言,但实际上它似乎做了更多的副本。
是否有可能在编译时获得更多的构造部分?
#include <iostream>
#include <cassert>
#include <string>
#include <sstream>
using namespace std;
// forward declaration
template <typename LHS> struct ME;
struct SE {
SE(char id, char n) : id(id), n(n), outer(true) {}
SE(const SE& expr) : id(expr.id), n(expr.n), outer(false) {}
ME<SE> operator,(const SE& r);
size_t size() const { return n; }
char *eval(char *b) const { b[0]=id; return b+1; }
~SE() { if(outer) { char b[20] = {}; char *p=eval(b); *p++='\n'; cout << b; } }
char id, n;
mutable bool outer;
};
template <typename LHS>
struct ME {
ME(const LHS& l, const SE& r)
: lhs(l), rhs(r), outer(true) // tentatively set to true
{ l.outer = r.outer = false; assert(l.size() == r.size()); } // reset flag for arguments
ME(const ME<LHS>& expr)
: lhs(expr.lhs), rhs(expr.rhs), outer(false) {}
size_t size() const { return rhs.size(); }
char *eval(char *b) const { *b++='('; b=lhs.eval(b); *b++=','; b=rhs.eval(b); *b++=')'; return b; }
auto operator,(const SE& r) { return ME<ME<LHS>>(*this, r); }
~ME() { if(outer) { char b[20] = {}; char *p=eval(b); *p++='\n'; cout << b; } }
LHS lhs;
SE rhs;
mutable bool outer;
};
ME<SE> SE::operator,(const SE& r) { return ME<SE>(*this, r); }
void demo(char a, char na, char b, char nb, char c, char nc) {
SE(a, na), SE(b,nb), SE(c,nc); // prints '((a,b),c)'
}
int main() {
demo('a',1,'b',1,'c',1);
return 0;
}
解决方案
您遵循的一般模式是表达式模板。阅读其他人的做法会有所帮助。
通常表达式模板大量使用 CRTP,并且不存储副本。
我相信我看到了由于副本引起的错误。
一般取用或T&&
存放。T&
T&&
通常,表达式模板在分配给目标时终止(并执行);你不想那样。由于 C++ 缺少 move-from-and-destroy,因此您必须在(名义上)运行时检查“不应执行”。
您可以存储指针并使用 null 作为“不运行”的情况,而不是引用/值和布尔值。
我无法弄清楚如何进行工作以确定要运行的内容constexpr
。不过也有可能。
推荐阅读
- ajax - 控制器 / ajax resquest 上的 HttpPostedFileBase null
- ios - 使用 Firebase 观察数据更新 UI (Swift)
- php - PHP 无限重定向
- azure - 用于在 Azure 门户上使用的配置 NLog.config 文件
- swift - 在 CollectionViewCell 上添加图层蒙版
- c++ - 返回值类型与函数类型不匹配(返回类型为嵌套类)
- jquery - 如何调试这个 JQuery 函数?
- matlab - 如何在 MATLAB 中绘制由归一化场模式给出的天线模式?
- ssh - 在获取 Visual Studio Code + Remote-SSH & Native debug 以连接到 openocd(即 gdbserver)时遇到问题
- python - 发送密钥而不在 python selenium webdriver 中指定元素(动作链不起作用)