Search code examples
c++templatestemplate-meta-programmingc++20concept

Passing a concept to a function


Since concepts are defined as compile-time predicates, is it also possible to actually reuse these predicates for compile-time algorithms? For example would it be possible to check whether all types in a tuple conform to a concept? As far as I have seen it is not possible to pass a concept to a function in any way, which kind of leads me back to using templates for these cases.

#include <type_traits>

template<typename T>
concept FloatLike = std::is_same_v<T, float>;

struct IsFloat
{
    template<typename U>
    constexpr static bool test()
    {
       return FloatLike<U>;
    }
};


template<typename Predicate, typename... T>
constexpr bool all_types()
{
    return (Predicate::template test<T>() && ...);
}


int main()
{
   static_assert(all_types<IsFloat, float, float>());
   static_assert(!all_types<IsFloat, float, int>());
}

What I would like to do is something like this, so I don't have to wrap the concept all the time to be able to use it:

template<concept Predicate, typename... T>
constexpr bool all_types()
{
    return (Predicate<T> && ...);
}


int main()
{
   static_assert(all_types<FloatLike, float, float>());
   static_assert(!all_types<FloatLike, float, int>());
}

Is there any way to get closer to this?


Solution

  • Is there any way to get closer to this?

    Well, no, not really. Not in C++20. There is no notion in the language today of a template concept-parameter. Even variable templates cannot be used as template parameters. So if have a concept to begin with, we can't avoid wrapping.

    But what we can do is write simpler wrappers. If we agree to use "old style" type traits as predicates, specifically those that behave like std::integral_constants, then we can have ourselves pretty terse "concept" definitions that can be used as predicates.

    template<typename T>
    using FloatLike = std::is_same<T, float>;
    
    template<template <typename> class Predicate, typename... T>
    constexpr bool all_types()
    {
        return (Predicate<T>{} && ...);
    }
    

    It's as good as it can get, as far as I can see.