【问题标题】:How to detect a pointer to an arithmetic type using type traits and concepts?如何使用类型特征和概念检测指向算术类型的指针?
【发布时间】:2021-01-23 20:47:03
【问题描述】:

如何编写一个检测算术类型指针的概念?

template <typename T>
concept arithmetic = std::is_arithmetic<T>::value;

template <typename T>
concept pointer_to_arithmetic = requires (T a) {
    { *a } -> arithmetic;
};

template <typename T>
void fn() {
    printf("fail\n");
}   

template <pointer_to_arithmetic T>
void fn() {
    printf("pass\n");
}   

struct s{};

int main() {
    fn<int>();
    fn<int*>();
    fn<s>();
    fn<s*>();
}

我尝试了上面的方法,它可以编译,但没有做它应该做的事情。

预期输出是:

fail
pass
fail
fail

相反,我得到:

fail
fail
fail
fail

如果我将*a 替换为a[0],它也不起作用。

【问题讨论】:

    标签: c++ c++20 typetraits c++-concepts


    【解决方案1】:

    对于复合需求中的表达式E,类型约束谓词被输入decltype((E))1。

    decltype 将表达式的值类别编码为其推导的类型。因为*p 是一个左值表达式。对于某些T,推断的类型为T&amp;。

    因此您可能希望将您的一对概念重写为

    template <typename T>
    concept arithmetic_ref = std::is_arithmetic<std::remove_reference_t<T>>::value;
    
    template <typename T>
    concept pointer_to_arithmetic = requires (T a) {
        { *a } -> arithmetic_ref ;
    };
    

    原子谓词的命名可能更好。


    当然,这留下了几个问题。你只是鸭子打字,所以任何类似指针的类型(甚至std::optional 有operator*)都是允许的?还是您只追求基本的指针类型?这个概念应该如何对待 cv 限定类型(它目前不允许它们)?

    根据您回答这些问题的方式,可以进一步调整概念。

    【讨论】:

    • 我注意到这允许重载operator*,例如pointer_to_arithmetic&lt;std::unique_ptr&lt;int&gt;&gt; 是真的。可能是有意的,也可能不是。
    【解决方案2】:

    这应该可以解决问题:

    template <typename T>
    concept pointer_to_arithmetic = requires (T a) {
        requires std::is_arithmetic_v<std::remove_cvref_t<decltype(*a)>>;
        requires std::is_pointer_v<T>;
    };
    
    ...
    
    pointer_to_arithmetic<int>          -> false
    pointer_to_arithmetic<float>        -> false
    pointer_to_arithmetic<std::string*> -> false
    pointer_to_arithmetic<int*>         -> true
    pointer_to_arithmetic<float*>       -> true
    
    

    第一行检测任何可以取消引用到算术类型的类型。这可能不是你需要的。所以你需要添加第二行来检测T是否确实是一个指针类型。

    【讨论】:

    • pointer_to_arithmetic&lt;std::string *&gt; -&gt; true,看起来不对。
    • 使用 _v 代替 ::value
    【解决方案3】:

    arithmetic trait 的类型是 int&amp; 和 s&amp;,而不是 int 和 s。解决此问题的一种方法是从 *a 表达式中删除引用:

    template<typename T>
    auto decay(T&& t) -> std::remove_cvref_t<T> {
        return t;
    }
    
    template <typename T>
    concept pointer_to_arithmetic = requires (T a) {
        { decay(*a) } -> arithmetic;
    };
    

    解决此问题的另一种方法是让arithmetic 将算术类型的引用识别为算术,因为您仍然可以以相同的方式使用变量。这在另一个答案中有所涉及,尽管对概念名称进行了更改。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2015-04-06
      • 1970-01-01
      • 1970-01-01
      • 2015-01-01
      • 2019-12-17
      相关资源
      最近更新 更多