Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

C++ concepts compound requirements and return-type-requirements

Tags:

c++

c++20

The last time I used C++ concepts with GCC and the fconcepts flag the following snippet used to work

template <typename T, typename U>
concept equality_comparable = requires(T a, U b) {
  { a == b } -> bool;
  { a != b } -> bool;
};

Apparently this is no longer the case and a return-type-requirement after a compound requirement can now only contain type constraints. If I'm not mistaken this basically means using another concept to satisfy the return-type-requirement.

So the perfectly readable and (for C++ standards) short snippet becomes

template <typename From, typename To>
concept convertible_to = std::is_convertible_v<From, To>;

template <typename T, typename U>
concept equality_comparable = requires(T a, U b) {
  { a == b } -> convertible_to<bool>;
  { a != b } -> convertible_to<bool>;
};

Of course this isn't even a full implementation, but let's ignore that for now. Could someone maybe explain to me why the committee decided to change that? Personally I find that "implicitly used template parameter" in the convertible_to concept extremely irritating and confusing.

like image 871
Vinci Avatar asked Apr 24 '20 14:04

Vinci


People also ask

What are concepts CPP?

The main uses of concepts are: introducing type-checking to template programming. simplified compiler diagnostics for failed template instantiations. selecting function template overloads and class template specializations based on type properties.

What are CPP constraints?

A constraint is a requirement that types used as type arguments must satisfy. For example, a constraint might be that the type argument must implement a certain interface or inherit from a specific class. Constraints are optional; not specifying a constraint on a parameter is equivalent to using a Object constraint.


1 Answers

Well, what does this actually mean:

template <typename T, typename U>
concept equality_comparable = requires(T a, U b) {
  { a == b } -> bool;
  { a != b } -> bool;
};

Does it mean a == b must have type exactly bool, or does it mean if you decay the type you get bool (i.e. const bool or bool& are ok), or does it mean convertible to bool (i.e. std::true_type is ok)? I don't think it's at all clear from the syntax - and any one of these three could be meaningfully desired by a particular concept (as P1452 points out, at the time, the ratio of Same<T> to ConvertibleTo<T> in concepts was 40-14).

The paper also goes on to point out that in the Concepts TS, where -> Type existed, we also had the ability to write something like vector<Concept>... or -> vector<Concept> as a requirement. That's a type, but would behave very difficultly with the decltype(()) semantics we adopted in P1084.

Basically I don't think the "perfectly readable" snippet actually is - there are multiple potential meanings for that syntax, all of which can be the desired meaning depending on context. And the most commonly used one at the time (same_as<bool>) isn't even the one we want here (convertible_to<bool>).


Personally I find that "implicitly used template parameter" in the convertible_to concept extremely irritating and confusing.

It's novel in C++, but I personally find it reads quite nicely in these cases. Seeing:

{ a == b } -> convertible_to<bool>;

Just reads exactly as the requirement: a == b needs to be a valid expression that's convertible to bool. For unary concepts, it makes the usage quite nice since you can use them in place of the somewhat meaningless typename/class keyword:

template <range R>
void algo(R&& r);

Which isn't that different from other languages. Like, in Rust for instance:

fn algo<I: Iterator>(i: I)

There the "implicitly used template parameter" is so implicit that it's not even part of the trait declaration, it's implicit there too:

pub trait Iterator { ... }

So even with a longer-form syntax, you'd write where I: Iterator whereas in C++ you'd still write requires range<R>.

This isn't strictly related to the original question, but I just find it interesting to add for some other color.

like image 52
Barry Avatar answered Sep 28 '22 00:09

Barry