There's a few options I can think of, each with their own ugliness.
One obvious option is to use pointers (probably unique_ptr
) instead of references. Of course, in order for this to work, it either requires allocation from the heap, or custom allocators. I think with a good allocator, this approach has some merits. Then again, the operator overloading would just get nasty.
Another approach is to store sub-expressions by value instead of by const reference. The efficiency of this approach is very compiler-dependant, but since you're basically dealing with a bunch of temporaries, I would imagine that modern compilers can optimize away the copies (or at least, a lot of the copies).
The last approach allows you to keep the same structure to your code, but forces the user to evaluate the expression. It requires that you have only one iterable type, which is the underlying type of the expression (say, std::vector<int>
). None of the expression classes should have begin
and end
methods or functions defined for them, but should just be convertible to the underlying type. This way, code like for(auto x : expr)
will fail at compile-time (since expr
is not iterable), but writing for(auto x : static_cast<vector<int>>(expr))
works because the expression is already evaluated.
If you were hoping to use range-based for loops to implement expression template operations, then you can provide private or protected begin
and end
methods in your expression template classes. Just make sure each template class can access the begin
and end
methods of the other template classes. It should be okay in this context since the expression template is a parameter to the function, so you won't have to worry about dangling references when writing the loop within that function.
auto&&
in the range-basedfor
loop might really turn out to be something to shoot oneself in the foot easily – I still haven't really understood what range types exactly are affected, and why (lvalue references to non-const: dangerous, non-references: unproblematic, ???). – Vincentiabegin/end
overrides that return input iterators. – Kalifbegin
andend
. – Reconvertstring("a") += string("b")
. – Vincentia