14

假设,给定 C++17if 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,并且将采用 the 的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; })
     ^~

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

4

3 回答 3

7

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

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

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

于 2017-11-06T20:11:47.267 回答
4

这是一个使用conceptinsideif 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>
}

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

于 2020-02-23T10:57:29.783 回答
3

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

于 2019-12-10T21:39:55.623 回答