非类型模板参数和要求
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-clause 和 requires-expression.
template<Bla b>
void f() requires .... {}
// ^^^^ - constant-expression OR
// requires-expression
//
// ^^^^^^^^^^^^^ - requires-clause
特别是,根据 [temp.pre]/1,requires-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-clause 和 constant-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() {}
我正在学习概念,我想不出一种方法来限制非类型模板参数的值(不是类型)。
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-clause 和 requires-expression.
template<Bla b>
void f() requires .... {}
// ^^^^ - constant-expression OR
// requires-expression
//
// ^^^^^^^^^^^^^ - requires-clause
特别是,根据 [temp.pre]/1,requires-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-clause 和 constant-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() {}