On 2021-05-02 18:56, Arthur O'Dwyer wrote:
The full hand-written example is:
requires (constructible_from<AlphableRef, T, Args...>
and not same_as<remove_cvref_t<T>, AlphableRef)
With my proposal
requires(value_constructible<AlphableRef, T, Args...>)
I am not too concerned about source code size though.
I want to provide a type trait that communicates intent.
I actually think the original communicates intent better.
One problem with having a trait is you need to name the trait. Maybe there's a really good name for the trait, but I don't think "value constructible" is it. We already have "value initialization", which this is not at all related to, and that's a real problem.
But the other problem is that the intent is... exactly to restrict same-type. I don't think there's a better way to express that intent other than literally that. For instance, let's take std::any. Which would have a constructor today like:
template <copy_constructible T>
requires (not is_specialization_of_v<remove_cvref_t<T>,
in_place_type>)
and (not same_as<remove_cvref_t<T>, any>)
any(T&&);
Personally, I think that communicates the intent really well. I'm annoyed that I have to do this thing at all, but given that I have to do it, I'm not sure this is especially bad?
Now, what does this look like with your trait? In this particular case, maybe we collapse together copy_constructible and the "not same_as"?
template <typename T>
requires value_constructible<any, XX, YY> and
(not
is_specialization_of_v<remove_cvref_t<T>,
in_place_type>)
any(T&&);
What do we put in XX and YY? XX kind of has to be remove_cvref_t<T>, but would YY be T? T const&? remove_reference_t<T> const&? It doesn't really... work that cleanly?
Now, "not same_as<remove_cvref_t<T>, U>" is a mouthful and is something that comes up a lot (as you point out). And we do have papers that propose concepts to streamline that part of the check (P2199 spells this "distinct_from<T, U>" and P2196 spells this "not similar<T, U>"). I think those are the right way to go here.
Barry