【问题标题】:if constexpr and requires-expression for ad-hoc concepts checkingif constexpr 和 requires-expression 用于临时概念检查
【发布时间】:2018-04-19 01:20:27
【问题描述】:

假设给定 C++17 的 if constexpr 和 Concepts TS(例如,在最近的 gcc 版本中),我们想检查模板函数中的类型是否具有嵌套类型:

#include <iostream>

struct Foo { using Bar = int; };

template<typename T>
void doSmth(T)
{
    if constexpr (requires { typename T::Bar; })
        std::cout << "has nested! " << typename T::Bar {} << std::endl;
    else
        std::cout << "no nested!" << std::endl;
}

int main()
{
    doSmth(Foo {});
    //doSmth(0);
}

关于概念的文档很少,所以我可能弄错了,但似乎就是这样(现场示例在 Wandbox)。

现在让我们考虑取消注释另一个doSmth 调用时会发生什么。期望 requires 子句评估为 false 似乎是合理的,并且将采用 if constexprelse 分支。与此相反,gcc 使这是一个硬错误:

prog.cc: In instantiation of 'void doSmth(T) [with T = int]':
prog.cc:17:13:   required from here
prog.cc:8:5: error: 'int' is not a class, struct, or union type
     if constexpr (requires { typename T::Bar; })
     ^~

这是 gcc 中的错误,还是预期的行为?

【问题讨论】:

标签: c++ c++-concepts c++20


【解决方案1】:

Concepts issue 3(“在更多情况下允许requires-expressions”)在 6 月获得了 WP 状态。从[expr.prim.req] 的当前外观来看,尤其是 p6:

将模板参数替换为 requires-expression 可能会导致在其需求中形成无效类型或表达式,或者违反这些需求的语义约束。在这种情况下,requires-expression 的计算结果为 false;它不会导致程序格式错误。

我会说你的代码很好,而 GCC 没有正确实现问题 3 的解决方案。

【讨论】:

  • 感谢您确认此语法没问题!问题是requires 表达式内部的失败会导致完全的硬失败,如果使用std::is_same_v 写出类似要求TFoo 的内容,它就可以正常工作。可悲的是,我对标准的了解还不够,无法弄清楚这是否已经遵循标准和提案,或者是否应该特别注意它。
  • @0xd34df00d 糟糕,我没有正确阅读问题。稍后我可能会看看,但现在看来这肯定不是问题(会破坏 requires 表达式本身的目的)。
  • @0xd34df00d 调整了答案。
  • 太好了,非常感谢!我应该更彻底地阅读您最初提到的段落。
【解决方案2】:

这是一个在if constexpr 中使用concept 的工作示例,用于检查一个类型是否具有方法foo 以及作为模板提供的特定返回类型T参数:

template<class P, class T>
concept Fooable = requires(P p) {
    requires std::same_as<decltype(p.foo()), T>;
};

template<typename T>
void printIsFooable(const auto& p) {
    if constexpr( Fooable<decltype(p), T> ) {
        std::cout << "fooable <" << typeid(T).name() << ">" << std::endl;
    }
    else {
        std::cout << "not fooable <" << typeid(T).name() << ">" << std::endl;
    }
}

struct MyFoo {
    void foo() const {}
};

int main() {
    printIsFooable<void>(MyFoo{}); // fooable <v>
    printIsFooable<int>(MyFoo{});  // not fooable <i>
    printIsFooable<void>(int{});   // not fooable <v>
}

代码使用 C++20 in GCCin Clang 编译。

【讨论】:

    【解决方案3】:

    它从 C++2a 和 gcc 10 开始工作: https://wandbox.org/permlink/qH34tI6oRJ3Ck7Mm

    【讨论】:

      猜你喜欢
      • 2020-11-29
      • 2019-07-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2021-10-05
      • 2021-11-27
      • 2020-10-28
      相关资源
      最近更新 更多