c++ - 使用表达式 : how to minimize runtime construction time

标签 c++ c++11 operator-overloading

我有两个类,一个表达式 (SE) 和两个表达式的集合 (ME)。束本身是一个表达式,因此它可以是另一个束的元素。

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;
};

bundle 的构造基于数据成员 n 执行简单的有效性检查,可在 ME 中通过方法 size 访问。 eval 方法使用数据成员 id 进行一些计算。 nid 在编译时都是未知的。

对于这两个类,我都覆盖了逗号运算符,以便它执行将多个单个表达式递归捆绑到嵌套 bundle 中。

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)'

实现这一目标的一种可能方法是使用类的析构函数并添加一个标志 is_outer,该标志在 SEME< 的构造期间适当更新。当这些类中的任何一个被破坏时,如果标志指示这是最外层类,则触发eval。下面给出了完整的演示。

测试 godbolt下面的简单 demo 函数,在我看来,编译器生成的代码比绝对必要的多。虽然 idn 在编译时是未知的,但表达式的最终类型应该是。我希望 bundle 的整个构造减少到只在正确的位置移动几个数字,然后检查断言,但它似乎实际上做了更多的拷贝。

是否有可能在编译时产生更多的构造部分?

#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++ 缺少移动和销毁,您必须在(名义上)运行时检查“不应执行”。

您可以存储指针并使用 null 作为“不运行”的情况,而不是引用/值和 bool。

我不知道如何确定要运行什么 constexpr。然而,这可能是可能的。

关于c++ - 使用表达式 : how to minimize runtime construction time,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/53957069/

相关文章:

operator-overloading - D opBinary()() 重载错误?

c++ - 将 char 转换为 stack<string> 的字符串

c++ - void(int) 和 void (*)(int) 之间的区别

c++ - lambda 中的 noexcept 在 vs2012 中如何工作?

c++ - `nullptr_t` 应该是全局命名空间的一部分吗?

c++ - 如何重载 operator= 以返回指向成员的指针?

c++ - 此代码对于重载比较运算符是否正确?

c++ - 在 libclang 访问者中将成员函数作为参数传递

c++ - 链接器实际上如何处理多重定义的 `inline` 函数?

c++ - unordered_map::clear() 释放c++中元素占用的内存?