This page is a snapshot from the LWG issues list, see theLibrary Active Issues List for more information and the meaning ofC++11 status.
partial_sum andadjacent_difference should mention requirementsSection: 26.10.7[partial.sum]Status:C++11Submitter: Marc SchooldermanOpened: 2006-02-06Last modified: 2016-01-28
Priority:Not Prioritized
View all issues withC++11 status.
Discussion:
There are some problems in the definition ofpartial_sum andadjacent_difference in 26.4 [lib.numeric.ops]
Unlikeaccumulate andinner_product, these functions are notparametrized on a "type T", instead, 26.4.3 [lib.partial.sum] simplyspecifies the effects clause as;
Assigns to every element referred to by iterator
iin the range[result,result + (last - first))a value correspondingly equal to((...(* first + *( first + 1)) + ...) + *( first + ( i - result )))
And similarly for BinaryOperation. Using just this definition, it seemslogical to expect that:
char i_array[4] = { 100, 100, 100, 100 };int o_array[4];std::partial_sum(i_array, i_array+4, o_array);Is equivalent to
int o_array[4] = { 100, 100+100, 100+100+100, 100+100+100+100 };i.e. 100, 200, 300, 400, with addition happening in theresult type,int.
Yet all implementations I have tested produce 100, -56, 44, -112,because they are using an accumulator of theInputIterator'svalue_type, which in this case ischar, notint.
The issue becomes more noticeable when the result of the expression*i +*(i+1) orbinary_op(*i, *i-1) can't be converted to thevalue_type. In a contrived example:
enum not_int { x = 1, y = 2 };...not_int e_array[4] = { x, x, y, y };std::partial_sum(e_array, e_array+4, o_array);Is it the intent that the operations happen in theinput type, or intheresult type?
If the intent is that operations happen in theresult type, somethinglike this should be added to the "Requires" clause of 26.4.3/4[lib.partial.sum]:
The type of
*i + *(i+1)orbinary_op(*i, *(i+1))shall meet therequirements ofCopyConstructible(20.1.3) andAssignable(23.1) types.
(As also required forT in 26.4.1 [lib.accumulate] and 26.4.2[lib.inner.product].)
The "auto initializer" feature proposed inN1894is not required toimplementpartial_sum this way. The 'narrowing' behaviour can still beobtained by using thestd::plus<> function object.
If the intent is that operations happen in theinput type, thensomething like this should be added instead;
The type of *first shall meet the requirements of
CopyConstructible(20.1.3) andAssignable(23.1) types.The result of*i + *(i+1)orbinary_op(*i, *(i+1))shall beconvertible to this type.
The 'widening' behaviour can then be obtained by writing a custom proxyiterator, which is somewhat involved.
In both cases, the semantics should probably be clarified.
26.4.4 [lib.adjacent.difference] is similarly underspecified, althoughall implementations seem to perform operations in the 'result' type:
unsigned char i_array[4] = { 4, 3, 2, 1 };int o_array[4];std::adjacent_difference(i_array, i_array+4, o_array);o_array is 4, -1, -1, -1 as expected, not 4, 255, 255, 255.
In any case,adjacent_difference doesn't mention the requirements on thevalue_type; it can be brought in line with the rest of 26.4[lib.numeric.ops] by adding the following to 26.4.4/2[lib.adjacent.difference]:
The type of
*firstshall meet the requirements ofCopyConstructible(20.1.3) andAssignable(23.1) types."
[Berlin: Giving output iterator's value_types very controversial. Suggestion ofadding signatures to allow user to specify "accumulator".]
[Bellevue:]
The intent of the algorithms is to perform their calculations using the type of the input iterator.Proposed wording provided.
[Sophia Antipolis:]
We did not agree that the proposed resolution was correct. For example,when the arguments are types
(float*, float*, double*), thehighest-quality solution would use double as the type of theaccumulator. If the intent of the wording is to require that the type ofthe accumulator must be theinput_iterator'svalue_type, the wordingshould specify it.
[2009-05-09 Alisdair adds:]
Now that we have the facility, the 'best' accumulator type could probably bededuced as:
std::common_type<InIter::value_type, OutIter::reference>::typeThis type would then have additional requirements of constructability andincrementability/assignability.
If this extracting an accumulator type from a pair/set of iterators (withadditional requirements on that type) is a problem for multiple functions,it might be worth extracting into a SharedAccumulator concept or similar.
I'll go no further in writing up wording now, until the group gives aclearer indication of preferred direction.
[2009-07 Frankfurt]
The proposed resolution isn't quite right. For example, "the type of
*first" should be changed to "iterator::value_type" or similar. Danielvolunteered to correct the wording.
[2009-07-29 Daniel corrected wording.]
[2009-10 Santa Cruz:]
Move to Ready.
Proposed resolution:
Change 26.10.7[partial.sum]/1 as indicated:
Effects:Let
VTbeInputIterator's value type. For a nonempty range,initializes an accumulatoraccof typeVTwith*firstand performs*result = acc. For every iteratoriin[first + 1, last)in order,accis thenmodified byacc = acc + *ioracc = binary_op(acc, *i)and is assignedto*(result + (i - first)).Assigns to every element referred to byiteratoriin the range[result,result + (last - first))a valuecorrespondinglyequal to((...(*first + *(first + 1)) + ...) + *(first + (i - result)))
orbinary_op(binary_op(..., binary_op(*first, *(first + 1)),...), *(first + (i - result)))
Change 26.10.7[partial.sum]/3 as indicated:
Complexity: Exactly
max((last - first) - 1, 0)applicationsofthe binary operation.binary_op
Change 26.10.7[partial.sum]/4 as indicated:
Requires:
VTshall be constructible from the type of*first, the result ofacc + *iorbinary_op(acc, *i)shall be implicitly convertible toVT, andthe result of the expressionaccshall be writable to theresultoutput iterator. In the ranges[first,last]and[result,result + (last - first)][..]
Change 26.10.12[adjacent.difference]/1 as indicated:
Effects:Let
VTbeInputIterator's value type. For a nonempty range,initializes an accumulatoraccof typeVTwith*firstand performs*result = acc. For every iteratoriin[first + 1, last)in order,initializes avaluevalof typeVTwith*i, assigns the result ofval - accorbinary_op(val, acc)to*(result + (i - first))and modifiesacc = std::move(val).Assigns to every element referred to by iteratoriin the range[result + 1,result + (last - first))a value correspondingly equal to*(first + (i - result)) - *(first + (i - result) - 1)
orbinary_op(*(first + (i - result)), *(first + (i - result) - 1)).
result gets the value of *first.
Change 26.10.12[adjacent.difference]/2 as indicated:
Requires:
VTshall beMoveAssignable([moveassignable])and shall beconstructible from the type of*first. The resultof the expressionaccand the result of the expressionval - accorbinary_op(val, acc)shall be writable to theresultoutput iterator. In the ranges[first,last][..]
Change 26.10.12[adjacent.difference]/5 as indicated:
Complexity: Exactly
max((last - first) - 1, 0)applicationsofthe binary operation.binary_op