I would like to understand better about overloading && and || operators and the loss of its short-circuit behaviour.
This question stems from my attempt at implementing a lazy-evaluated data value holder of template type T. Consider such a struct, e.g.
template <typename T>
struct Value {
  // value holder & accessor
  T m_val;
  virtual T getValue() {return m_val}; // trivial, but potentially complicated computation in derived classes
  // will come into play below
  template <typename U, typename V>
  struct LogicalAnd;
};
And the value of T that any instance holds is accessed by getValue(). The types I am interested in are primarily arithmetic data types, and the use case for this class will be that its value can be accessed by getValue(), but also that I could define an arbitrary derived class MyValue<T> later down the road that could perform some heavy computation to store & return different values throughout the runtime of the program.
Given that, I'd like to be able to "book" operations, e.g. &&, between two instances to reflect their lazily-updated values. A straightforward implementation of operator overloading does not achieve this, since it will return the basic data type value as evaluated at that line:
// overload && operator
template <typename U, typename V>
bool operator&&(Value<U>& a, Value<V>& b) {
    return (a && b);    
}  // returns a basic data type bool evaluated at above line. X
So, I instead can write a proxy class representing the operation that I want and overload the operator such that it returns an instance of the proxy.
// addition of two existing Value
template <typename T>
template <typename U, V>
struct Value<T>::LogicalAnd {
  virtual T getValue() override {
    return m_valA->getValue() && m_valB->getValue();
  }
  Value<U>* m_valA;
  Value<V>* m_valB;
};
// overload && operator
template <typename U, typename V>
auto operator&&(Value<U>& a, Value<V>& b) {
    return typename Value<decltype(a.m_val && b.m_val)>::template LogicalAnd<U,V>{&a, &b};          
}  // returns a Value<X>::LogicalAnd<U,V>, X is most likely bool.
Then, I can get the lazy evaluation I want by calling getValue() of this proxy instance:
Value<double> a{1.0};
Value<int> b{0.0};
c = a&&b; // c is Value<bool>::LogicalAnd<double,int>
c.getValue(); // = false
b.m_val = 1.0; // update the value of b
c.getValue(); // = true!!!
The question is regards to whether this would still be considered losing short-circuit behaviour of the operation. As I see it, it should be at least effectively preserved, as the built-in && operator is being used by LogicalAnd implementation and heavy computation is done on-the-fly by the operands' getValue() methods. In other words, whenever I call c.getValue(), if a.getValue() is already false, then b.getValue() would not be called. Would a use case such as this warrant a "good-enough" practice for overloading these operators?
 
     
    