如何在约束中使用ADL?

问题描述

作为示例,我想使用约束来确保为模板参数isinf实现函数T。如果Tfloatdoublelong 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;子句移至全局名称空间。但这将isinf引入了全局命名空间,我想避免这种情况。

  • 使用概念定义将using std::isinf;子句封装在名为ABC的命名空间中,然后在命名空间之后直接添加using ABC::Test;。这似乎有点不可思议。

有更好的解决方案吗?

解决方法

这种事情在Ranges中的工作方式是创建一个Customization Point Object。这紧密地反映了您的第二个选项(我们在自定义名称空间中粘贴了using-声明),除了我们还提供了一种机制,使用户可以调用正确的isinf,而不必自己编写一堆相同的样板。 / p>

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概念的指定方式。

,

这应该有效。我对c ++ 20概念不太满意,所以不能保证语法正确。

namespace impl {

using std::isinf;
template<class T>
auto adl_isinf(T t) -> decltype(isinf(t));
// { return isinf(t); } // implementation not necessary depending on whether
                        // requires is an unevaluated context
}

template <typename T>
concept Test = requires (T a) {
    impl::adl_isinf(a);
};