2

How can we convert the requirements In this question link to a concept

I have attempted the following:

template< typename U, typename Tin, typename Tout>
concept MyConditions =
    (
         U::value_type
        &&  Tin::value_type
        &&  Tout::value_type
        && std::is_floating_point_v<typename Tin::value_type>
        && std::is_integral_v<typename U::value_type>
        && std::is_floating_point_v<typename Tout::value_type>
    );

This concept is now applied to one of my member functions:

class test_concept
{
template< typename U, typename Tin, typename Tout>
requires MyConditions <U, Tin, Tout>
static void test_routine(const U&, const Tin&, Tout& );
}

When testing:

std::vector<double> test{ };
std::vector<int> testi{ };
std::vector<double> test2{ };

test_concept::test_routine(testi, test, test2);

Using clang I get the error message that no matching were found, and a note saying:

note: because substituted constraint expression is ill-formed: missing 'typename' prior to dependent type name 'vector<int, allocator >::value_type' U::value_type

ATK
  • 1,296
  • 10
  • 26

1 Answers1

5

You don't need to manually check for the presence of the types. If they're not there, SFINAE will cause your concept to silently return false anyway. So:

template< typename U, typename Tin, typename Tout>
concept MyConditions =
    std::is_integral_v<typename T::value_type> &&
    std::is_floating_point_v<typename U::value_type> &&
    std::is_floating_point_v<typename Tout::value_type>;

But if you want to explicitly check for the types, here's the syntax:

template< typename U, typename Tin, typename Tout>
concept MyConditions =
    requires
    {
        typename U::value_type;
        typename Tin::value_type;
        typename Tout::value_type;
    } &&
    std::is_integral_v<typename T::value_type> &&
    std::is_floating_point_v<typename U::value_type> &&
    std::is_floating_point_v<typename Tout::value_type>;

You can also move all conditions into the requires:

template< typename U, typename Tin, typename Tout>
concept MyConditions =
    requires
    {
        typename U::value_type;
        typename Tin::value_type;
        typename Tout::value_type;
        requires std::is_integral_v<typename T::value_type>;
        requires std::is_floating_point_v<typename U::value_type>;
        requires std::is_floating_point_v<typename Tout::value_type>;
    };

Also you should prefer the standard concepts to the old traits:

template< typename U, typename Tin, typename Tout>
concept MyConditions =
    requires
    {
        typename U::value_type;
        typename Tin::value_type;
        typename Tout::value_type;
        requires std::integral<typename T::value_type>;
        requires std::floating_point<typename U::value_type>;
        requires std::floating_point<typename Tout::value_type>;
    };
HolyBlackCat
  • 78,603
  • 9
  • 131
  • 207
  • Thanks for the different options!. What do you mean by prefer standard concepts? Should we prefer `std::integral` over `std::integral_v`? If yes, then why? – ATK Apr 17 '21 at 13:14
  • 2
    @A2LBK Yes, that's what I meant. I don't think it makes a difference here, but in some cases it does. See https://stackoverflow.com/questions/52062386/does-constraint-subsumption-only-apply-to-concepts – HolyBlackCat Apr 17 '21 at 13:18
  • 2
    While you could move all conditions into the `requires`, it's not a good idea to do this blindly because it turns your concept from a recognized conjunction of atomic constraints into a one big blob. Taking the concept from the question, this means that `MyConditions` is no longer more specific than any of its `std::integral/floating_point` pieces, so you can no longer overload a `MyConditions` function with a `std::integral` function and have the overload resolution work as you'd expect. – chris Apr 17 '21 at 20:42