[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]
Re: [lmi] Specializing std::numeric_limits?
From: |
Greg Chicares |
Subject: |
Re: [lmi] Specializing std::numeric_limits? |
Date: |
Sun, 7 Mar 2021 13:36:06 +0000 |
User-agent: |
Mozilla/5.0 (X11; Linux x86_64; rv:78.0) Gecko/20100101 Thunderbird/78.4.0 |
On 3/6/21 11:41 PM, Vadim Zeitlin wrote:
> On Sat, 6 Mar 2021 23:24:09 +0000 Greg Chicares <gchicares@sbcglobal.net>
> wrote:
[...]
> GC> Vadim--Is std::numeric_limits as rarely specialized in practice as I
> GC> infer from the meagerness of stackexchange discussions on this topic?
>
> I don't really know, but IMHO it doesn't make much sense to specialize it
> for user-defined types, even if it is indeed allowed as some people seem to
> think. I've always thought of this class as providing information about the
> properties of arithmetic types of the concrete implementation and from this
> point of view specializing it would only be useful if you could also define
> new arithmetic types, but you can't do this.
Interesting--yes, it can be seen as a C++-ization of the C macros that
define properties of built-in arithmetic types. From that perspective,
we probably wouldn't define a full complement of macros like, say,
#define CURRENCY_MAX ...
#define CURRENCY_DIG ...
and a specialization like
std::numeric_limits<currency>
would be no better than the macros it would wrap.
> GC> At any rate, lmi's class template minmax was originally intended to
> provide
> GC> a "safe" cover for std::minmax_element(), hiding the pointers (which until
> GC> today could have been null, so it wasn't safe) behind functions.
>
> Sorry, I couldn't understand this part. Which pointers do you mean here?
std::minmax_element() returns std::pair<ForwardIterator,ForwardIterator>,
and I loosely referred to the forward iterators as "pointers".
> GC> Then it
> GC> was enhanced, using std::numeric_limits to set a priori extrema...which
> GC> were not "meaningful" according to the Standard in all actual use cases
> GC> (fixed now). So we could have instantiated minmax<some_random_class>
> GC> whose minimum() and maximum() would both have either returned zero,
> GC> or segfaulted.
>
> If this class is only supposed to be used with arithmetic types (or maybe
> even only floating point ones? AFAICS it's only used with double outside of
> the tests), shouldn't we (statically) assert this fact directly?
It could just as well be used for integers. There's no need to forbid
that, even though lmi hasn't yet used it that way.
> GC> I think I've fixed the problems now, without specializing numeric_limits,
> GC> except that the "maximum" double is DBL_MAX, and I know a double value
> GC> that exceeds DBL_MAX (and one less than the "minimum", -DBL_MAX).
>
> Again, I don't know what is the intention here, i.e. whether we actually
> want to use the infinities here. Mathematically it would make sense, of
> course, but I'd have to spend much more time studying the code to
> understand if it would be better or worse for the existing users of this
> class.
I take the inverse viewpoint: if existing clients of this class use it
in a way that does not make mathematical sense, then those clients are
defective.
> BTW, on a completely different note, looking at this code:
>
> auto const& extrema = std::minmax_element(v.begin(), v.end());
> minimum_ = *extrema.first ;
> maximum_ = *extrema.second;
>
> I think it could be one of the few cases where std::tie() could be actually
> useful, as it would allow writing this as
>
> std::tie(minimum_, maximum_) = std::minmax_element(v.begin(), v.end());
>
> which is IMHO slightly more readable.
But that doesn't dereference the iterators:
/usr/lib/gcc/i686-w64-mingw32/8.3-win32/include/c++/tuple:1252:25:
error: cannot convert ‘__gnu_cxx::__normal_iterator<const int*,
std::vector<int> >’ to ‘int’ in assignment
this->_M_head(*this) = std::forward<_U1>(__in.first);
/usr/lib/gcc/i686-w64-mingw32/8.3-win32/include/c++/tuple:1253:40:
error: cannot convert ‘__gnu_cxx::__normal_iterator<const int*,
std::vector<int> >’ to ‘int’ in assignment
this->_M_tail(*this)._M_head(*this) = std::forward<_U2>(__in.second);
and changing the type of minmax::minimum_ and minmax::maximum_
to iterator (i.e., pointer) violates one of the class's design
goals, which is to have no pointer members that might be null.
And I don't think "structured bindings" can be used to set data members, e.g.:
[minimum_, maximum_] = std::minmax_element(v.begin(), v.end());