if constexpr和requires-expression用于ad-hoc概念检查

0xd*_*00d 7 c++ c++-concepts c++20

让我们说,给定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);
}
Run Code Online (Sandbox Code Playgroud)

概念的文档很少,所以我可能错了,但似乎就是这样(并且实例是在Wandbox上).

现在让我们考虑一下取消注释另一个doSmth调用时会发生什么.似乎有理由期望require-clause将评估false,并且将采取意志的else分支if constexpr.与此相反,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; })
     ^~
Run Code Online (Sandbox Code Playgroud)

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

Col*_*mbo 6

概念问题 3(“允许在更多上下文中使用requires-expression ”)在 6 月获得 WP 状态。从[expr.prim.req]的当前外观来看,尤其是 p6:

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

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


Ami*_*rsh 6

concept下面是一个使用insideif constexpr来检查类型是否具有方法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>
}
Run Code Online (Sandbox Code Playgroud)

代码在 GCCClang 中使用 C++20 进行编译。


Dmi*_*hov 5

它从 C++2a 和 gcc 10 开始工作:

#include <iostream>

struct Foo { using Bar = char; };

template<typename T> void doSmth(T)
{
    if constexpr (requires { typename T::Bar; })
        std::cout << "T has Bar of type: " << typeid(typename T::Bar).name() << std::endl;
    else
        std::cout << "T does not have Bar" << std::endl;
}

int main()
{
    doSmth(Foo {});
    doSmth(1);
}
Run Code Online (Sandbox Code Playgroud)

https://wandbox.org/permlink/qH34tI6oRJ3Ck7Mm