13

I am learning concepts, and I can not figure out a way to restrain the value (not type) of non-type template parameter.

Example of code that compiles, although I wish it did not(due to failed requirement):

#include <cassert>

enum Bla{
    Lol, 
    Haha
};

template<Bla b>
requires requires{
    // my guess is that this just checks that this is valid expression, not
    // that it is true
    b>1; 
}
void f(){
    assert(b>1);
}

int main() {
    f<Lol>(); // compiles, not funny ;)
}

Note: this is simplified example( I want "template overloading") so static_assert is not good for me, and I am trying to avoid std::enable_if since syntax is hideous.

NoSenseEtAl
  • 28,205
  • 28
  • 128
  • 277

3 Answers3

8

If you only have a boolean condition and nothing else, do this:

template<Bla b>
requires(b > 1)
void f() {}

Alternative longer syntax, if you need to check more things in the same requires-expression:

template<Bla b>
requires requires
{
    requires b > 1;
//  ^~~~~~~~
}
void f() {}
HolyBlackCat
  • 78,603
  • 9
  • 131
  • 207
6

Since f needs to be constrained only by the value of the non-type template parameter, you can simply write a requires clause instead of an ad-hoc requires requires constraint:

template<Bla b>
requires (b>1) 
void f() {}

Here's a demo.

You only need a requires requires expression if you want to do more complicated checks on the template parameter. In that case, I recommend using a named concept over an ad-hoc constraint anyway. This makes the code more readable, and allows you to reuse the concept in other places.


As for assert, it's a run-time construct, so it doesn't affect compilation in any way, assuming the expression inside the assert is syntactically valid. You need to use static_assert instead, if you want to check the template parameter at compile time:

static_assert(b>1);
cigien
  • 57,834
  • 11
  • 73
  • 112
  • @idclev463035818 is right, assert is just to show that code dies at runtime... Your solution is great, but now I do not get why I only need 1 requires :) , but yeah that is outside of the scope of this question. – NoSenseEtAl Nov 16 '20 at 12:51
  • @NoSenseEtAl `requires requires` is a ad-hoc constraint. It's only used with requires expressions, not requires clauses. Yeah, that should probably be a different question. – cigien Nov 16 '20 at 12:54
3

Constraints, requires-clause:s and requires-expression:s

You need to differentiate between a requires-clause and a requires-expression.

template<Bla b>
void f() requires .... {}
//                ^^^^ - constant-expression OR
//                       requires-expression
//       
//       ^^^^^^^^^^^^^ - requires-clause 

Particularly, as per [temp.pre]/1, the grammar for a requires-clause is:

requires-clause:
  requires constraint-logical-or-expression

where constraint-logical-or-expression, in turn, is a primary-expression, which includes requires-expression:s.

Applied to OP:s example: constraining a non-type template parameter

In your case, you are using an ad-hoc requires-expression (as compared to a named constraint) as the requirement for a requires-clause. However, for your use case it suffices to use a requires-clause with a constant-expression. Particularly, you can restrain the value of a non-type template parameter by a constant expression in the trailing requires-clause of a given templated entity, say a function template:

enum class MyEnum {
    Foo,
    Bar,
    Baz
};

// Allows any specialization over MyEnum.
template<MyEnum e>
struct Wrapped {};
    
// Allows only Wrapped objects of certain
// specializations.
template<MyEnum e>
void f(Wrapped<e>) requires (e == MyEnum::Foo || e == MyEnum::Bar) {
}

int main() {
    f(Wrapped<MyEnum::Foo>{}); // OK
    f(Wrapped<MyEnum::Bar>{}); // OK
    f(Wrapped<MyEnum::Baz>{}); // Error: ... constraints not satisfied
}

Applied for overloading with mutually exclusive constraints:

// Allows any specialization over MyEnum.
template<MyEnum e>
struct Wrapped {};
    
// Overloading Wrapped specializations by 
// mutually exclusive constraints:
template<MyEnum e>
void f(Wrapped<e>) requires (e == MyEnum::Foo || e == MyEnum::Bar) {
    std::cout<< __PRETTY_FUNCTION__ << "\n";
}

template<MyEnum e>
void f(Wrapped<e>) requires (e == MyEnum::Baz) {
    std::cout<< __PRETTY_FUNCTION__ << "\n";
}

int main() {
    f(Wrapped<MyEnum::Foo>{}); // void f(Wrapped<e>) requires  e == MyEnum::Foo || e == MyEnum::Bar [with MyEnum e = MyEnum::Foo]
    f(Wrapped<MyEnum::Bar>{}); // void f(Wrapped<e>) requires  e == MyEnum::Foo || e == MyEnum::Bar [with MyEnum e = MyEnum::Bar]
    f(Wrapped<MyEnum::Baz>{}); // void f(Wrapped<e>) requires  e == MyEnum::Baz [with MyEnum e = MyEnum::Baz]
}
dfrib
  • 70,367
  • 12
  • 127
  • 192