On 17/04/2023 10:52, Alf P. Steinbach wrote:
> On 2023-04-17 6:15 AM, Bonita Montero wrote:
>> template<typename T>
>> concept scalar = is_scalar_v<T>;
>>
>> template<scalar ... TS>
>> common_type_t<TS ...> min( TS ... values )
>> {
>> common_type_t<TS ...> min = get<0>( tuple<TS ...>( values ... ) );
>> ((min = values < min ? values : min), ...);
>> return min;
>> }
>
> I guess you're posting this because of a perceived elegance in the code.
>
> What is the point of the initialization of `min` when the first thing
> that happens after is that `min` is assigned to?
It is needed because, given arbitrary types, there is no general choice
of the initial value for the fold. (It is not a "C++ fold" because C++
only supports folds on infix binary operators, but it is a "fold" in the
more general programming sense.)
But the code then re-uses the first value, which is unnecessary - if
comparisons are expensive, it is a waste. It is better to use a
head+tail recursion pattern here:
template<scalar T, scalar ... TS>
auto xmin(T t, TS ... values )
{
auto x = xmin(values ...);
return t < x ? t : x;
}
A t3(A a, B b, C c) {
return xmin(a, b, c);
}
>
> Was perhaps the intent to reduce the number of assignments via e.g.
>
> ((values < min ? min = values : 0), ...);
>
No - see above.
> And why do you name a local variable the same as the function?
To confuse readers, I expect. I assume it is the same reason there
appears to be a "using std;" in effect, and then a function whose name
matches one in the "std" namespace (i.e., "std::min").
>
> One reason to write your own `min` rather than just using `std::min`
> could be to support a mix of signed and unsigned. But the presented code
> doesn't do that, so (because it supports a mix of arbitrary scalar
> argument types) it introduces a bug vector compared to `std::min`. Why?
>
Mixing signed and unsigned types is always a risk in C++. In a reusable
function like this, it could make sense to protect against it.
Otherwise, gcc's "-Wconversion -Wsign-conversion" are useful tools.