First, the "stronger type alias":
using U = new T;
As you've realized (good!), the problems are going to be with T's existing customization points:
- std::swap(u, u)
- std::hash<T>(u)
With this declaration:
[...]
- Any implicit conversion to T (e.g. from constructor) is
also an implicit conversion to U.
- There's an implicit conversion from U to T.
- But there's no implicit conversion from T to U.
- Any function which takes a T can also take a U, unless
explicitly deleted.
- But any function which takes a U can't take a T.
T's copy constructor is an "implicit conversion to T" — I mean, if T is std::string and U is Name, then
T t = "Yves";
void tfoo(T);
tfoo(t); // OK, implicitly calls T(const T&)
void ufoo(U);
ufoo(t); // OK, would have implicitly called T(const T&), so there must be a U(const T&) by the first bullet point quoted above
using Name = new std::string;
void Store(std::string the_name); // (1)
void Store(Name the_name); // (2)
/* error */ Store("Your name here"); // ambiguous,
ill-formed
FWIW, this is mildly surprising; this would be a place where `std::is_base_of_v<T, U>` and yet the signature `void(U)` is not considered more-specialized-than `void(T)`.
But I think this is just an indication that your intuition about std::is_base_of is wrong. Types with no inheritance relationships (no base classes) definitely should not claim to have is_base_of relationships.
/* ok */ using Names_Income =
std::unordered_map<Name, double>;
// std::hash<std::string> used because of
// implicit cast from Name to std::string
If there's an implicit conversion from Name to std::string, then why did you say
std::string s;
s = name;
didn't work?
See
HTH,
Arthur