c++c++20c++-conceptsnon-type-template-parameterc++-templates

Can concepts be used to put a constraint on values as well as types?


Concepts can be used to put a constraint on types as template parameters like the example below:

template<typename t, int v>
concept the_concept1 = sizeof(t) > v;

template<int v, the_concept1<v> t>
struct some_struct1{};

I am trying to use a similar method with values like the example below:

template<int v1, int v2>
concept the_concept2 = v1 > v2;

template<int v1, the_concept2<v1> v2>
struct some_struct2{};

But with G++ 10 I am getting the following error message:

error: ‘the_concept2’ does not constrain a type

So I was wondering if concepts can be used to put a constraint on values? If so then how should I do it?

Edit: My final goal is to use the concept in declaration of a template structure with variadic template parameters like:

template<typename t, std::size_t ... v>
struct the_struct;

And I need a concept to check if every v is less than sizeof(t).


Solution

  • If you want to use a concept as a named type constraint on a template parameter, as in your example, the concept needs to apply to a type template parameter.

    You can still define concepts that apply only to e.g. non-type template parameters, however, as long as you use it in a context which allows these; e.g. using a requires-clause:

    template<int v1, int v2>
    concept the_concept2 = v1 > v2;
    
    template<int v1, int v2> requires the_concept2<v1, v2>
    struct some_struct2{};
    
    using valid = some_struct2<42, 41>;
    //using invalid = some_struct2<42, 42>; // constraints not satisfied
    

    Another example applied on a function template or a member function of a class template:

    template<int v1, int v2>
    concept the_concept2 = v1 > v2;
    
    template <int a, int b>
    void bar() requires the_concept2<a, b> {} 
    
    template <int a, int b>
    struct Foo {
        static void bar() requires the_concept2<a, b> {} 
    };
    
    int main() {
        bar<2, 1>();
        Foo<2, 1>::bar();
        //bar<2, 2>();      // candidate template ignored: constraints not satisfied
        //Foo<2, 2>::bar(); // invalid reference to function 'bar': constraints not satisfied
    }
    

    The following OP edit (which basically asks an entirely different question)

    Edit: My final goal is to use the concept in declaration of a template structure with variadic template parameters like:

    template<typename t, std::size_t ... v>
    struct the_struct;
    

    And I need a concept to check if every v is less than sizeof(t).

    can be achieved by specifying the concept itself to apply for variadic non-type template parameters that are expanded in the sizeof(T) > v check using parameter pack expansion:

    #include <cstddef>
    #include <cstdint>
    
    template<typename T, std::size_t... v>
    concept the_concept1 = (... && (sizeof(T) > v));
    
    template<typename T, std::size_t... vs> requires the_concept1<T, vs...>
    struct the_struct;
    
    using TypeOfSize4Bytes = uint32_t;
    
    using valid = the_struct<TypeOfSize4Bytes, 1, 3, 2, 1>;
    using also_valid = the_struct<TypeOfSize4Bytes>;
    //using invalid = the_struct<TypeOfSize4Bytes, 1, 2, 4>;  // error: constraints not satisfied