我发现表达式模板here的出色解释。在本文中,我们找到了一种用于算术的基本表达式模板实现,如下(略有改动):
#include <iostream>
template <typename T>
struct plus {
T operator()(const T a, const T b) const { return a + b; }
};
template <class ExprT>
struct exprTraits {
typedef ExprT expr_type;
};
template <class ExprT1, class ExprT2, class BinOp>
class BinaryExpr {
public:
BinaryExpr(ExprT1 e1, ExprT2 e2, BinOp op = BinOp()) : _expr1(e1), _expr2(e2), _op(op) {}
double eval() const { return _op(_expr1.eval(), _expr2.eval()); }
private:
typename exprTraits<ExprT1>::expr_type _expr1;
typename exprTraits<ExprT2>::expr_type _expr2;
BinOp _op;
};
class Literal {
public:
Literal(const double v) : _val(v) {}
double eval() const { return _val; }
private:
const double _val;
};
template <>
struct exprTraits<double> {
typedef Literal expr_type;
};
class Variable {
public:
Variable(double& v) : _val(v) {}
double eval() const { return _val; }
void operator+=(double x) { _val += x; }
private:
double& _val;
};
class SpecialVariable : public Variable {
public:
SpecialVariable(double& v) : Variable{v} {};
double eval() const { return -1000.0; }
};
template <class ExprT1, class ExprT2>
BinaryExpr<ExprT1, ExprT2, plus<double>> operator+(ExprT1 e1, ExprT2 e2) {
return BinaryExpr<ExprT1, ExprT2, plus<double>>(e1, e2);
}
存在三种类型的节点,Literal
,Variable
和SpecialVariable
,后者是后者的子类。特质允许表达式中包含double
之类的内置类型,而无需用Literal
包装它们。
现在,假设我想添加double
和Variable
并将其加分配到Variable
时要做一些特别的事情。我将以下成员函数添加到Variable
:
void operator+=(BinaryExpr<double, Variable, plus<double>> expr) { _val += 1000.0; }
并编写一些测试程序:
int main(int argc, char const* argv[]) {
double xd = 2.0, yd = 5.0;
Variable x{xd};
SpecialVariable y{yd};
x += 3.0 + y;
std::cout << "result : " << std::to_string(x.eval()) << "\n";
return 0;
}
但是,这仅适用于Variable
s,而不适用于SpecialVariable
s,即我收到以下编译器错误:
error: no match for ‘operator+=’ (operand types are ‘Variable’ and ‘BinaryExpr<double, SpecialVariable, plus<double> >’) x += 3.0 + y;
note: no known conversion for argument 1 from ‘BinaryExpr<double, SpecialVariable, plus<double> >’ to ‘BinaryExpr<double, Variable, plus<double> >’
这是完全合理的,因为如果模板类的模板参数有一个,则它们不一定具有关系。
问题:如何编写一个operator+=
接受类型为和可能是其子类型的表达式模板?我还没有看到解决此特定问题的表达式模板教程。
答案 0 :(得分:4)
问题:我该如何编写一个
operator+=
来接受带有类型及其子类型的表达式模板?
使用std::is_base_of
和SFINAE
template <typename V>
std::enable_if_t<std::is_base_of_v<Variable, V>>
operator+= (BinaryExpr<double, V, plus<double>> expr)
{ _val += 1000.0; }
前面的代码在C ++ 17中编译。
如果您使用的是C ++ 14,则必须使用
std::is_base_of<Variable, V>::value
代替
std::is_base_of_v<Variable, V>
如果您使用的是C ++ 11,则必须使用
typename std::enable_if<std::is_base_of<Variable, V>::value>::type
代替
std::enable_if_t<std::is_base_of_v<Variable, V>>