例如,我想使用一个约束来确保为模板参数isinf
实现函数T
。如果T
是float
,double
,long double
或整数类型之一,则可以通过以下方式完成此操作:
#include <cmath>
template <typename T>
concept Test = requires (T a) {
std::isinf(a);
};
但是,我也想将此约束用于实现我自己的isinf
函数的自定义非标准数据类型。该函数未包含在std
命名空间中,因此我尝试了以下操作:#include <cmath>
template <typename T>
concept Test = requires (T a) {
using std::isinf;
isinf(a);
};
这是行不通的,因为require子句中的每个语句都应该是有效的要求,而using std::isinf
根本不是“要求”。我看到两个解决此问题的方法:
using std::isinf;
子句移到全局 namespace 。但这将isinf
引入了全局命名空间,我想避免这种情况。using std::isinf;
子句封装在名为ABC
的 namespace 中,然后在该 namespace 之后直接添加using ABC::Test;
。这似乎有点不可思议。有更好的解决方案吗?
最佳答案
这种事情在Ranges中的工作方式是创建一个Customization Point Object。这紧密地反射(reflect)了您的第二个选项(我们在自定义 namespace 中粘贴了using-声明),除了我们还提供了一种机制,使用户可以调用正确的isinf
,而不必自己编写一堆相同的样板文件。isinf
的定制点对象如下所示:
namespace N {
// make our own namespace
namespace impl {
// ... where we can bring in std::isinf
using std::isinf;
struct isinf_t {
// our type is constrained on unqualified isinf working
// in a context where std::isinf can be found
template <typename T>
requires requires (T t) {
{ isinf(t) } -> std::same_as<bool>;
}
constexpr bool operator()(T t) const {
// ... and just invokes that (we know it's valid and bool at this point)
return isinf(t);
}
};
}
// we provide an object such that `isinf(x)` incorporates ADL itself
inline constexpr auto isinf = impl::isinf_t{};
}
现在我们有了一个对象,直接遵循一个概念:template <typename T>
concept Test = requires (T t) {
N::isinf(t);
}
这就是 range
概念的确切指定方式。