考虑CRTP的标准用法,用于某些表达式模板机制,它通过值保留其子项:
template <typename T, typename>
struct Expr {};
template <typename T>
struct Cst : Expr<T, Cst<T>>
{
Cst(T value) : value(std::move(value)) {}
private:
T value;
};
template <typename T, typename L, typename R>
struct Add : Expr<T, Add<T, L, R>>
{
Add(L l, R r) : l(std::move(l)), r(std::move(r))
private:
L l; R r;
};
等等
现在,在实现运算符时,我们必须通过引用传递,因为参数要向下转换为正确的类型。问题是我发现自己实现了四个(!)版本operator+
:
template <typename T, typename L, typename R>
Add<T, L, R> operator+(Expr<T, L>&& l, Expr<T, R>&& r)
{
return Add<T, L, R>(
std::move(static_cast<L&>(l)),
std::move(static_cast<R&>(r)));
}
template <typename T, typename L, typename R>
Add<T, L, R> operator+(const Expr<T, L>& l, Expr<T, R>&& r)
{
return Add<T, L, R>(
static_cast<const L&>(l),
std::move(static_cast<R&>(r)));
}
template <typename T, typename L, typename R>
Add<T, L, R> operator+(Expr<T, L>&& l, const Expr<T, R>& r)
{
return Add<T, L, R>(
std::move(static_cast<L&>(l)),
static_cast<const R&>(r));
}
template <typename T, typename L, typename R>
Add<T, L, R> operator+(const Expr<T, L>& l, const Expr<T, R>& r)
{
return Add<T, L, R>(
static_cast<const L&>(l),
static_cast<const R&>(r));
}
实际上,如果目标是尽量减少不必要的复制,则必须区分临时值(可以移动)和左值(必须复制),因此有四种重载。
在 C++03 中,“没问题”:我们使用 const 引用并一直复制,期间。在 C++11 中,我们可以做得更好,这就是这里的目标。
是否有一些技巧可以让我编写一次加法逻辑,或者在这里编写一个宏是我的最佳选择(因为其他运算符将重复该逻辑)?
我也愿意接受有关如何使用 C++11 编写表达式模板的其他建议。只需考虑目标是最小化复制,因为存储在终端节点中的值可能是巨大的数字或矩阵(在我的精确情况下,终端节点可能包含几兆字节的插值数据,并且这些对象的复制被禁用 - 对于其他对象,复制是可能的)。