admin管理员组

文章数量:1410696

using the following code

template <typename T>
concept SomeConcept = requires(T t) {
    { t++ } -> std::convertible_to<T>;
};

The concept SomeConcept ensures that the type T has overloaded the operator++(int) and the result type is convertible to T. And std::convertible_to takes 2 parameters the first is From, and the second is To.

So my question is, which parameter takes the result of the expression and why? and by that answer why is T is passed as the other parameter and not the other way around?

please provide a suitable reference to backup the answer, preferably from the standard draft if possible.

Thanks in advance

using the following code

template <typename T>
concept SomeConcept = requires(T t) {
    { t++ } -> std::convertible_to<T>;
};

The concept SomeConcept ensures that the type T has overloaded the operator++(int) and the result type is convertible to T. And std::convertible_to takes 2 parameters the first is From, and the second is To.

So my question is, which parameter takes the result of the expression and why? and by that answer why is T is passed as the other parameter and not the other way around?

please provide a suitable reference to backup the answer, preferably from the standard draft if possible.

Thanks in advance

Share edited Mar 9 at 19:37 Ahmed AEK 19.8k3 gold badges17 silver badges42 bronze badges asked Mar 9 at 19:24 MuhammadMuhammad 1,6953 gold badges22 silver badges33 bronze badges
Add a comment  | 

3 Answers 3

Reset to default 5

From cppreference:

In a type-constraint, a concept takes one less template argument than its parameter list demands, because the contextually deduced type is implicitly used as the first argument of the concept.

template<class T, class U>
concept Derived = std::is_base_of<U, T>::value;
 
template<Derived<Base> T>
void f(T); // T is constrained by Derived<T, Base>

So, even though std::convertible_to takes two template parameters, the deduced type will be used as the first, which is the From parameter in your case.

The kind of requirement here:

    { t++ } -> std::convertible_to<T>;

is called a compound-requirement. Those are defined in [expr.prim.reqpound], which tells us two things:

  • that std::convertible_to<T> is a type-constraint — those are explained in `[temp.param]
  • that the "immediately-declared constraint" is decltype((E)).

The example that follows makes it clear what it means:

Given concepts C and D,

requires {
  { E1 } -> C;
  { E2 } -> D<A1, ..., An>; 

is equivalent to

requires {
  E1; requires C<decltype((E1))>;
  E2; requires D<decltype((E2)), A1, ..., An>;
}

Note the double parens: it's decltype((E)), not decltype(E).


That is, while convertible_to is a binary concept and it looks like we're only providing one type to it, in reality we're expressing:

template <typename T>
concept SomeConcept = requires(T t) {
    t++;
    requires std::convertible_to<decltype((t++)), T>;
};

I'll try to explain this.

In the expression { t++ } -> std::convertible_to<T>, the result type of t++ becomes the From parameter of std::convertible_to, while T is the To parameter.

This is because std::convertible_to<From, To> checks if From can be converted to To. In our case, we want to verify that the result of the postfix increment operation (which is typically a temporary object containing the original value) can be converted back to the type T.

The standard concept std::convertible_to is defined as:

template<typename From, typename To>
concept convertible_to = 
    is_convertible_v<From, To> &&
    requires { static_cast<To>(declval<From>()); };

So when we write { t++ } -> std::convertible_to<T>, we're effectively saying "the result of t++ must be convertible to T", which is exactly what we want to check for a well-behaved postfix increment operator.

本文标签: corder of passing parameters to templates in conceptsStack Overflow