Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Expression templates and ranged based for in C++11

It is my understanding that expression templates will break on ranged based for in C++11, as for (auto x : expr) has an implicit auto&& __range = expr in it, and this will result in dangling references.

Is there a way create expression template classes so that they either behave correctly with ranged based for, or at the very least throw a compile error?

Basically, I'd like to prevent the possibility that expression templates would correctly compile but fail at run time due to dangling references. I don't mind having to wrap expression templates in something before using them in a ranged based for, as long as there are no silent run-time errors if the user forgets to wrap the expression templates.

like image 543
Clinton Avatar asked Mar 01 '12 01:03

Clinton


People also ask

What are range-based for loops?

Range-based for loop (since C++11) Executes a for loop over a range. Used as a more readable equivalent to the traditional for loop operating over a range of values, such as all elements in a container.

What is a range expression C++?

Range-based for loop in C++ range-expression − any expression used to represent a sequence of elements. Also Sequence of elements in braces can be used. loop-statement − body of for loop that contains one or more statements that are to be executed repeatedly till the end of range-expression.

How do you create a range in CPP?

Use the range-based for statement to construct loops that must execute through a range, which is defined as anything that you can iterate through—for example, std::vector , or any other C++ Standard Library sequence whose range is defined by a begin() and end() .


2 Answers

There's generally nothing you can do about this. If you give an expression as the range, it must resolve to something that will be valid after the initialization of the for statement. And there's no way to detect at compile-time that any particular type was deduced by auto.

It would be better to make your expression system more move-based, so that it doesn't have to hold references. That will yield much safer results with auto than trying to store references to potentially dead things. If the copying for non-movable types troubles you, then just live with it.

like image 166
Nicol Bolas Avatar answered Sep 28 '22 06:09

Nicol Bolas


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.

like image 27
Ken Wayne VanderLinde Avatar answered Sep 28 '22 06:09

Ken Wayne VanderLinde