【问题标题】:How to use ADL in Constraints?如何在约束中使用 ADL?
【发布时间】:2020-11-07 00:23:48
【问题描述】:

例如,我想使用一个约束来确保函数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);
};

这不起作用,因为 requires 子句中的每个语句都应该是有效的要求,而 using std::isinf 根本不是“要求”。

我看到了解决此问题的两种解决方法:

  • using std::isinf; 子句移至全局命名空间。但这会将isinf 引入全局命名空间,我想避免这种情况。

  • using std::isinf; 子句与概念定义封装在名为ABC 的命名空间中,然后在命名空间后直接添加using ABC::Test;。这似乎有点奇怪。

有没有更好的解决方案?

【问题讨论】:

    标签: c++ c++20 c++-concepts argument-dependent-lookup


    【解决方案1】:

    这种事情在 Ranges 中的工作方式是创建一个自定义点对象。这与您的第二个选项非常相似(我们在自定义命名空间中添加了 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 概念的指定方式。

    【讨论】:

    • 这看起来像我的答案,但有更多步骤。假设用户为他的类型启用了isinf ADL,那么使用这种方法比我的建议有什么好处?
    • @Mestkon 它为您提供了一个对象N::isinf,您可以使用它来进行调度。因为它是一个对象,你可以做一些事情,比如将它传递给算法......所以就像ranges::any_of(r, N::isinf) 一样工作。
    • 嗯,没错。不必以更高的复杂性为代价编写[](auto t) { return adl_isinf(t); } 有一点好处。取决于用户需要更多。 godbolt.org/z/KEbjGG
    • 感谢您的回答。这比我希望的要多得多的代码行,但至少它在其余代码中保持简单。作为旁注,第 12 行复合要求中的分号应根据 gcc 和 en.cppreference.com/w/cpp/language/constraints 移动到行尾
    【解决方案2】:

    这应该可行。我对 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);
    };
    

    【讨论】:

    • 这对我来说基本上就像 OP 的第二个解决方法。
    • 是的。在主命名空间中定义概念而不是使用 using 语句导入它的区别可能足以消除 OP 正在谈论的一些“怪异”。
    • 是的,我更喜欢您的解决方案而不是我的第二个解决方法。我测试了你的代码并且语法是正确的,但你在第 3 行写了using std::inf 而不是using std::isinf。我无法编辑你的答案,因为更改将少于 6 个字符。
    猜你喜欢
    • 2016-11-09
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2012-09-15
    • 1970-01-01
    • 1970-01-01
    • 2021-02-02
    • 1970-01-01
    相关资源
    最近更新 更多