if constexpr 和 requires-expression 用于临时概念检查
if constexpr and requires-expression for ad-hoc concepts checking
假设,给定 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 constexpr
的 else
分支将被采用。与此相反,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 中的错误,还是预期的行为?
概念issue 3 ("Allow requires-expressions in more contexts") was given WP status in June. And judging by the current looks of [expr.prim.req],尤其是 p6:
The substitution of template arguments into a requires-expression may result in the formation of invalid types or expressions in its requirements or the violation of the semantic constraints of those requirements. In such cases, the requires-expression evaluates to false
; it does not cause the program to be ill-formed.
我会说你的代码很好,GCC 没有正确实现问题 3 的解决方案。
它从 C++2a 和 gcc 10 开始工作:
https://wandbox.org/permlink/qH34tI6oRJ3Ck7Mm
这是一个在 if constexpr
中使用 concept
的工作示例,用于检查类型是否具有具有特定 return 的方法 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++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 constexpr
的 else
分支将被采用。与此相反,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 中的错误,还是预期的行为?
概念issue 3 ("Allow requires-expressions in more contexts") was given WP status in June. And judging by the current looks of [expr.prim.req],尤其是 p6:
The substitution of template arguments into a requires-expression may result in the formation of invalid types or expressions in its requirements or the violation of the semantic constraints of those requirements. In such cases, the requires-expression evaluates to
false
; it does not cause the program to be ill-formed.
我会说你的代码很好,GCC 没有正确实现问题 3 的解决方案。
它从 C++2a 和 gcc 10 开始工作: https://wandbox.org/permlink/qH34tI6oRJ3Ck7Mm
这是一个在 if constexpr
中使用 concept
的工作示例,用于检查类型是否具有具有特定 return 的方法 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>
}