On Tue, 02 Dec 2014 08:49:30 -0500
Richard Damon <Ric...@Damon-Family.org> wrote:
> There is an argument that a = b should evaluate b first, and then
> process a to do the assignment. Also, given a + b * c some would say
> that the natural order to evaluate things would be b, c, b*c, a,
> a+b*c. These sort of expectations make a strict left to right become
> "surprising" at times. Yes, some languages will attempt to reduce
> undefined behavior by defining an order, inevitably this will
> sometimes be surprising to users, and will often force the compiler
> to generate less efficient code. One of the guiding principles of C
> that C++ inherited was that efficiency was important, and the
> compiler was allowed to assume that the programmer isn't trying hard
> to cause problems (if the programmer isn't following the rules, it is
> ok to bite them). This causes C and C++ to have perhaps more
> undefined behavior conditions than other languages, but also explains
> their popularity, programmers LIKE languages that trust them and
> generate faster results because of that. (Sometime when debugging
> problems you might wish for help, and since "undefined behavior" can
> include catching the situation and calling you a dummy, sometimes you
> can turn on debug modes/tools to help you.
I agree with that for the most part, but what annoys me most about C++
with regard to this, and I think offers little or no optimization
opportunities in return, is not unordered evaluation as such, but the
permit to partially evaluate. Take a function:
void func(std::unique_ptr<T1> a, std::unique_ptr<T2> b);
In the C++11 world (which does not have make_unique) the following code
represents a possible memory leak in the presence of an exception:
func(std::unique_ptr<T1>{new T1{val1}},
std::unique_ptr<T2>{new T2{val2}});
The problem with this is not that the order of evaluation of the
arguments is unspecified, but because the compiler is entitled to
partially evaluate by first evaluating the 'new T1{val1}' expression and
then evaluating the new 'T2{val2}' expression (or vice versa) before
constructing either unique_ptr object. This is OK with C but vastly
annoying with a language with exceptions such as C++. For exception
safety you have to do explicit sequencing yourself by hand, viz:
auto t1 = std::unique_ptr<T1>{new T1{val1};
auto t2 = std::unique_ptr<T2>{new T2{val2};
func(std::move(t1),
std::move(t2));
which is a pain in the butt.
I would really like to see that dealt with. The std::unique_ptr issue
is mostly solved by C++14's make_unique, but there are many other cases
where you might want to use handles where similar issues arise.
Chris