非类型模板参数和要求

Non-type template parameters and requires

我正在学习概念,我想不出一种方法来限制非类型模板参数的值(不是类型)。

Example 编译的代码,虽然我希望它没有(由于失败的要求):

#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 ;)
}

注意: 这是一个简化的例子(我想要“模板重载”)所以 static_assert 对我不好,我试图避免 std::enable_if 因为语法很糟糕。

因为 f 只需要被非类型模板参数的值约束,你可以简单地写一个 requires 子句而不是临时的 requires requires 约束:

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

这是 demo.

如果你想对模板参数做更复杂的检查,你只需要一个requires requires表达式。在这种情况下,我建议无论如何都使用命名概念而不是临时约束。这使代码更具可读性,并允许您在其他地方重用该概念。


至于 assert,它是一个 运行 时间构造,因此它不会以任何方式影响编译,假设 assert 中的表达式在语法上是有效的。如果要在编译时检查模板参数,则需要使用 static_assert 代替:

static_assert(b>1);

约束、requires-clause:s 和 requires-expression:s

您需要区分 requires-clauserequires-expression.

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

特别是,根据 [temp.pre]/1requires-clause 的语法是:

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

其中 constraint-logical-or-expression 又是 primary-expression, which includes requires-expression:s.

应用于OP:s示例:约束非类型模板参数

在您的情况下,您正在使用临时 requires-expression(与命名约束相比)作为 requires-clause 的要求。但是,对于您的用例,使用 requires-clauseconstant-expression 就足够了。特别是,您可以通过给定模板化实体的尾随 requires-clause 中的常量表达式来限制非类型模板参数的值,例如函数模板:

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
}

申请了具有互斥约束的重载:

// 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]
}

如果您只有布尔条件而没有其他条件,请执行以下操作:

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

替代的更长的语法,如果你需要在同一个 requires-expression 中检查更多的东西:

template<Bla b>
requires requires
{
    requires b > 1;
//  ^~~~~~~~
}
void f() {}