【问题标题】:Non-type template parameters and requires非类型模板参数和要求
【发布时间】:2021-02-27 15:57:12
【问题描述】:

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

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 因为语法很可怕。

【问题讨论】:

  • static_assert 会使其失败,但我想这只是简化示例
  • @idclev463035818 我想要它用于重载,所以是的。

标签: c++ c++20 c++-concepts non-type-template-parameter


【解决方案1】:

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

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

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

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

【讨论】:

  • 所以对这个问题的最高投票评论找到了答案? :P stackoverflow.com/questions/54200988/… 接受这一点,因为尽管 cigen 答案较早,但您提供了一种方法来做更复杂的事情......
  • Why stop 只在另一个 requires-expression 中嵌套一个? ;)
  • @dfrib 你刚刚发现__PRETTY_FUNCTION__ 的 GCC 错误。 :P
  • @HolyBlackCat 什么错误?缺少空间?
  • @NoSenseEtAl 是的。
【解决方案2】:

由于f 只需要受非类型模板参数值的约束,您可以简单地编写一个requires 子句而不是一个临时的requires requires 约束:

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

这是demo

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


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

static_assert(b>1);

【讨论】:

  • @idclev463035818 是对的,断言只是为了表明代码在运行时死亡......你的解决方案很棒,但现在我不明白为什么我只需要 1 个要求 :),但是是的超出了这个问题的范围。
  • @NoSenseEtAl requires requires 是一个临时约束。它仅与 requires 表达式一起使用,而不与 requires 子句一起使用。是的,这应该是一个不同的问题。
【解决方案3】:

约束、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,其中包括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]
}

【讨论】:

    猜你喜欢
    • 2021-03-18
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2011-08-06
    • 1970-01-01
    相关资源
    最近更新 更多