The short circuiting behaviour of the operators && and || is an amazing tool for programmers.
But why do they lose this behaviour w
Lambdas is not the only way to introduce laziness. Lazy evaluation is relatively straight-forward using Expression Templates in C++. There is no need for keyword lazy and it can be implemented in C++98. Expression trees are already mentions above. Expression templates are poor (but clever) man's expression trees. The trick is to convert the expression into a tree of recursively nested instantiations of the Expr template. The tree is evaluated separately after construction.
The following code implements short-circuited && and || operators for class S as long as it provides logical_and and logical_or free functions and it is convertible to bool. The code is in C++14 but the idea is applicable in C++98 also. See live example.
#include
struct S
{
bool val;
explicit S(int i) : val(i) {}
explicit S(bool b) : val(b) {}
template
S (const Expr & expr)
: val(evaluate(expr).val)
{ }
template
S & operator = (const Expr & expr)
{
val = evaluate(expr).val;
return *this;
}
explicit operator bool () const
{
return val;
}
};
S logical_and (const S & lhs, const S & rhs)
{
std::cout << "&& ";
return S{lhs.val && rhs.val};
}
S logical_or (const S & lhs, const S & rhs)
{
std::cout << "|| ";
return S{lhs.val || rhs.val};
}
const S & evaluate(const S &s)
{
return s;
}
template
S evaluate(const Expr & expr)
{
return expr.eval();
}
struct And
{
template
S operator ()(const LExpr & l, const RExpr & r) const
{
const S & temp = evaluate(l);
return temp? logical_and(temp, evaluate(r)) : temp;
}
};
struct Or
{
template
S operator ()(const LExpr & l, const RExpr & r) const
{
const S & temp = evaluate(l);
return temp? temp : logical_or(temp, evaluate(r));
}
};
template
struct Expr
{
Op op;
const LExpr &lhs;
const RExpr &rhs;
Expr(const LExpr& l, const RExpr & r)
: lhs(l),
rhs(r)
{}
S eval() const
{
return op(lhs, rhs);
}
};
template
auto operator && (const LExpr & lhs, const S & rhs)
{
return Expr (lhs, rhs);
}
template
auto operator && (const LExpr & lhs, const Expr & rhs)
{
return Expr> (lhs, rhs);
}
template
auto operator || (const LExpr & lhs, const S & rhs)
{
return Expr (lhs, rhs);
}
template
auto operator || (const LExpr & lhs, const Expr & rhs)
{
return Expr> (lhs, rhs);
}
std::ostream & operator << (std::ostream & o, const S & s)
{
o << s.val;
return o;
}
S and_result(S s1, S s2, S s3)
{
return s1 && s2 && s3;
}
S or_result(S s1, S s2, S s3)
{
return s1 || s2 || s3;
}
int main(void)
{
for(int i=0; i<= 1; ++i)
for(int j=0; j<= 1; ++j)
for(int k=0; k<= 1; ++k)
std::cout << and_result(S{i}, S{j}, S{k}) << std::endl;
for(int i=0; i<= 1; ++i)
for(int j=0; j<= 1; ++j)
for(int k=0; k<= 1; ++k)
std::cout << or_result(S{i}, S{j}, S{k}) << std::endl;
return 0;
}