对于实际的指针类型,用于检测类似(可引用)类型的指针的模板功能失败 [英] Template function for detecting pointer like (dereferencable) types fails for actual pointer types

查看:60
本文介绍了对于实际的指针类型,用于检测类似(可引用)类型的指针的模板功能失败的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我正在尝试编写一种机制来检测类型是否为 pointer like 类型。我的意思是,它可以通过 operator *() operator->()取消引用。

I am trying to write a mechanism to detect if a type is a pointer like type. By that I mean it is dereferencable through operator*() and operator->().

我有三种不同的结构进行相应的专门化:

I have three different structs that are specialized accordingly:


  1. is_pointer_like_dereferencable 检查运算符*()

  2. is_pointer_like_arrow_dereferencable 检查 operator->()

  3. is_pointer_like 只需将1& 2

  1. is_pointer_like_dereferencable which checks for operator*()
  2. is_pointer_like_arrow_dereferencable which checks for operator->()
  3. is_pointer_like which simply combines 1 & 2

我为非模板类​​型添加了特殊化,例如 int,int *,... 以及类似 std :: vector< ...>,std :: shared_ptr< ...>,... 的模板类型。

I added specializations for non-templated types like int, int*, ... and for templated types like std::vector<...>, std::shared_ptr<...>, ....

但是,似乎在实现 is_pointer_like_arrow_dereferencable 时犯了一个错误。相关代码为

However, it seems that I made a mistake when implementing is_pointer_like_arrow_dereferencable. The relevant code is

template <typename T, typename = void>
struct is_pointer_like_arrow_dereferencable : std::false_type 
{
};

template <typename T>
struct is_pointer_like_arrow_dereferencable<T, std::enable_if_t<
                                                std::is_pointer_v<T> ||
                                                std::is_same_v<decltype(std::declval<T>().operator->()), std::add_pointer_t<T>>>
    > : std::true_type
{
};


template <template <typename...> typename P, typename T, typename... R>
struct is_pointer_like_arrow_dereferencable<P<T, R...>, std::enable_if_t<
                                                std::is_same_v<decltype(std::declval<P<T, R...>>().operator->()), std::add_pointer_t<T>>>
    > : std::true_type
{
};

template <typename T>
constexpr bool is_pointer_like_arrow_dereferencable_v = is_pointer_like_arrow_dereferencable<T>::value;

第二个结构应检查非模板类型是实际指针类型还是该类型确实有一个箭头运算符,它返回一个指向自身的指针。但是,当我用下面的代码测试该机制时,指针类型(如 int * 无法正确检测到)。为什么?

The 2nd struct should check if a non-templated type is either an actual pointer type or if the type does have an arrow operator that returns a pointer to itself. But when I test the mechanism with the code below, pointer types (like int* are not detected correctly). Why is that?

template <typename T>
struct Test
{
    T& operator*()
    {
        return *this;
    }

    T* operator->()
    {
        return this;
    }
};   

void main()
{
    bool
        a = is_pointer_like_arrow_dereferencable_v<int>, // false
        b = is_pointer_like_arrow_dereferencable_v<int*>, // false, should be true
        c = is_pointer_like_arrow_dereferencable_v<vector<int>>, // false
        d = is_pointer_like_arrow_dereferencable_v<vector<int>*>, // false, should be true
        e = is_pointer_like_arrow_dereferencable_v<Test<int>>, // true
        f = is_pointer_like_arrow_dereferencable_v<Test<int>*>, // false, should be true
        g = is_pointer_like_arrow_dereferencable_v<shared_ptr<int>>, // true
        h = is_pointer_like_arrow_dereferencable_v<shared_ptr<int>*>, // false, should be true
        i = is_pointer_like_arrow_dereferencable_v<int***>; // false
}

is_pointer_like_dereferencable 结构仅在 std :: is_same_v< ...> 部分有所不同,并且确实可以正确检测实际的指针类型。

The is_pointer_like_dereferencable struct only differs at the std::is_same_v<...> part and it does detect actual pointer types correctly.

无法检测指针类型的事实(应该由 std :: is_pointer_v< ...> )对我来说没有任何意义。有人可以解释吗?

The fact that it fails to detect pointer types (which should be covered by std::is_pointer_v<...>) doesn't make any sense to me. Can someone explain this?

推荐答案


但是当我用下面的代码测试该机制时,指针类型(例如无法正确检测到int *)。为什么?

But when I test the mechanism with the code below, pointer types (like int* are not detected correctly). Why is that?

SFINAE:替换失败不是错误

S.F.I.N.A.E.: Substitution Failure Is Not An Error

因此,对于 int * ,来自 decltype(std :: declval< T>()。operator->()替换失败,并且不考虑专业化,因此使用一般形式,因此 std :: false

So, for int*, from decltype(std::declval<T>().operator->() you get a substitution failure and the specialization isn't considered. So is used the general form, so std::false

您应该编写两个专业化:一个或一个指针,另一个用于 operator->()启用的类。

You should write two specializations: one or pointers and one for operator->() enabled classes.

奖励答案:我建议您通过一组帮助函数(而不是 is_pointer_like_arrow_dereferencable (过于复杂,恕我直言)键入特征)

Bonus answer: instead of type traits as is_pointer_like_arrow_dereferencable (overcomplicated, IMHO), I propose you to pass through a set of helper functions (only declared)

template <typename>
std::false_type is_pointer_like (unsigned long);

template <typename T>
auto is_pointer_like (int)
   -> decltype( * std::declval<T>(), std::true_type{} );

template <typename T>
auto is_pointer_like (long)
   -> decltype( std::declval<T>().operator->(), std::true_type{} );

因此 is_pointer_like_arrow_dereferencable 可以简单地写为使用

template <typename T>
using is_pointer_like_arrow_dereferencable = decltype(is_pointer_like<T>(0));

使用助手 is_pointer_like_arrow_dereferencable_v

template <typename T>
static auto const is_pointer_like_arrow_dereferencable_v
   = is_pointer_like_arrow_dereferencable<T>::value;

以下是一个完整的示例

#include <type_traits>
#include <iostream>
#include <memory>
#include <vector>

template <typename>
std::false_type is_pointer_like (unsigned long);

template <typename T>
auto is_pointer_like (int)
   -> decltype( * std::declval<T>(), std::true_type{} );

template <typename T>
auto is_pointer_like (long)
   -> decltype( std::declval<T>().operator->(), std::true_type{} );

template <typename T>
using is_pointer_like_arrow_dereferencable = decltype(is_pointer_like<T>(0));

template <typename T>
static auto const is_pointer_like_arrow_dereferencable_v
   = is_pointer_like_arrow_dereferencable<T>::value;


template <typename T>
struct Test
 {
   T & operator*  () { return *this; }
   T * operator-> () { return  this; }
 }; 

int main()
 {
   std::cout << is_pointer_like_arrow_dereferencable_v<int>
      << std::endl, // false
   std::cout << is_pointer_like_arrow_dereferencable_v<int*>
      << std::endl, // true
   std::cout << is_pointer_like_arrow_dereferencable_v<std::vector<int>>
      << std::endl, // false
   std::cout << is_pointer_like_arrow_dereferencable_v<std::vector<int>*>
      << std::endl, // true
   std::cout << is_pointer_like_arrow_dereferencable_v<Test<int>>
      << std::endl, // true
   std::cout << is_pointer_like_arrow_dereferencable_v<Test<int>*>
      << std::endl, // true
   std::cout << is_pointer_like_arrow_dereferencable_v<std::shared_ptr<int>>
      << std::endl, // true
   std::cout << is_pointer_like_arrow_dereferencable_v<std::shared_ptr<int>*>
      << std::endl, // true
   std::cout << is_pointer_like_arrow_dereferencable_v<int***>
      << std::endl; // true
 }

这篇关于对于实际的指针类型,用于检测类似(可引用)类型的指针的模板功能失败的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆