C++ Logo

std-proposals

Advanced search

Re: Explicit using --> Library for simple strong types

From: Peter Sommerlad (C++) <"Peter>
Date: Wed, 27 Jan 2021 11:31:11 +0100
FWIW, I created a very simple to use strong typing framework for C++17
that allows to create strong types with respective selection of
appropriate operators on a single line, such as

struct Word:strong<std::string,Word, Out, Add, Order>{};

struct literGas:strong<double,literGas,Additive,Order,Out>{
   constexpr static inline auto suffix=" l";
  };
the suffix static member is for output customization.

Available at https://github.com/PeterSommerlad/PSsst

So I see little need in the language change, but I also did not follow
the discussed details.

Regards
Peter.

BAILLY Yves via Std-Proposals wrote on 27.01.21 11:22:
> I think I get your point. After more thinking about it (which I should
> have done before, sorry for that), then in short, I would say when writing
>
> *using U = new T;*
>
> …then yes, *U* is a new type different from *T* – although referring to
> the same Platonic type as *T*.
>
> This implies that indeed, *std::is_same<T,U>* is different from
> *std::is_same<T,T>* and *std::is_same_v<T,U> == std::is_same_v<U,T> ==
> false*.
>
>> For example:
>
>> template<class A> void f(A a);
>
>> f(T{}); // #1
>
>> f(U{}); // #2
>
>> Does #1 call f<T>? (Yes, it must.)
>
> Yes.
>
>> Does #2 call f<U>? (I don't know but I think so.)
>
> Yes, because it’s given a *U*.
>
>> Is f<U> the same function as, or a different function from, f<T>? (I don't know but I think so.)
>
> If *T* and *U* are two different types, then *f<U>* is a different
> function from *f<T>*.
>
> /However,/ because *U* and *T* refer to the same Platonic type and a *U*
> can be seen as a *T*, then if *f<U>* is syntactically correct,
> well-formed with regard to the restrictions put on U (for example,
> inside *f<>* there’s no assignment of a *T* to a *U* without an explicit
> cast), then the actual instantiation of *f<U>* can be the same as the
> instantiation of *f<T>* - in a way, *f<U> = f<T>*. Put in another way,
> if we assume *f<T>* has already been created, then the compiler would
> create a new “envelop” named *f<U>* but with the same contents as *f<T>*
> – unless of course some explicit specialization *f<U>* is provided by
> the user.
>
> This would apply to the *std::hash<>* specialization: when required to
> instantiate *std::hash<U>*, if it has not been explicitly specialized,
> then as you said the compiler may realize that *std::hash<U>* is the
> same (has the same contents although it doesn’t have the same identity)
> as *std::hash<T>* - again, if and only if the code for *std::hash<U>* is
> well-formed and there’s no explicit specialization defined provided by
> the user.
>
>> using Height = new int;
>
>> using Tilt = new int;
>
>> void increaseAltitude(Height h);
>
>> void increaseAttitude(Tilt t);
>
>> ...
>
>> Height h;
>
>> increaseAttitude(h); // oops — but, since increaseAttitude takes a Tilt, this happily won't compile!
>
>>
>
>> However, it doesn't seem to protect us against
>
>> Height h;
>
>> Tilt t;
>
>> t += h; // oops — and this silently calls the built-in candidate operator+=(int&, int)
>
> That’s true indeed, no more protection here than when using
> “traditional” typedefs. And I see how this can indeed be seen as a
> “massive inconsistency” between *t=h* (not OK) and *t+=h* (OK).
>
> However, I would argue the situation is still better than when using
> “standard” using declarations: at least the function call typo will be
> caught.
>
> If protection in above situations is desired, a solution would be to
> explicitly delete the operations we don’t want:
>
> *Tilt& operator+=(Tilt&,Height const&) = delete;*
>
> This would protect against the code you suggested, but the following
> would still be valid:
>
> *int h2; // “int”, not “Height”*
>
> *t += h2; // OK – and maybe oops, or not*
>
> Another solution would be to use the other suggested construct:
>
> *using Height = explicit int;*
>
> *using Tilt = explicit int;*
>
> *Height h{1}; // OK*
>
> *Tilt t{2}; // OK*
>
> *t += 1; // OK – literal used*
>
> *t += h; // ill-formed here thanks to the “explicit”*
>
> With the “explicit” construct, there’s no implicit interaction between T
> and U.
>
> Regards,
>
> *Yves Bailly**
> *Development engineer
> Manufacturing Intelligence division
> *Hexagon*
> *M: *+33 (0) 6.82.66.09.01
> HexagonMI.com
> <https://eur02.safelinks.protection.outlook.com/?url=https%3A%2F%2Fwww.hexagonmi.com%2Ffr-FR%2F&data=02%7C01%7C%7C9382ad0642f34df9564908d8007bc6c4%7C1b16ab3eb8f64fe39f3e2db7fe549f6a%7C0%7C0%7C637259878894792630&sdata=VS4%2B3Oc4yGnnDiF7m8x%2BiD6u5GnRjS634jv1suVUhrU%3D&reserved=0>
>
>
>


-- 
Peter Sommerlad
Better Software: Consulting, Training, Reviews
Modern, Safe & Agile C++
peter.cpp_at_[hidden]
+41 79 432 23 32

Received on 2021-01-27 04:31:18