前向声明 智能指针_C++ 指针类中实现 ->*

C++ 的裸指针是提供 ->* 的运算符的。但 C++ 智能指针目前是不提供 ->* 的运算符的。因为实现起来比较麻烦,而且没有必要。本文来实现一个,需要用到 C++17。

这篇文章本身在可能价值不大,只是一个探索。但是解决它所用到的工具,还是挺有用的。

->* 是啥

比如我们现在有类型 U,以及指向 U 成员的裸指针 rawp。

#include <iostream>

class U
{
public:
    int test = 0;
    void func()
    {
        std::cout << "func called" << std::endl;
        ++test;
    }
};

int main()
{
    U u;
    U *rawp = &u;

    auto pfunc = &U::func;
    (rawp->func)();
    (*rawp).func();
    (rawp->*pfunc)();
    ((*rawp).*pfunc)();

    auto ptest = &U::test;
    std::cout << rawp->test << std::endl;
    std::cout << (*rawp).test << std::endl;
    std::cout << rawp->*ptest << std::endl;
    std::cout << (*rawp).*ptest << std::endl;
    return 0;
}

那么通过指针访问 u 成员的办法,一般有这么四种(就是上面的顺序):

  1. 通过 -> 访问
  2. 通过 * 解引用,再利用 . 来访问
  3. 通过 ->* 访问。->* 操作符的右边是一个指向类成员的指针
  4. 通过 * 先把指针解引用,再利用 .* 访问。.* 的操作符的右边是一个指向类成员的指针

但是如果把 rawp 换成智能指针,那么上面第 3 个访问 u 的办法就失效了,因为智能指针没有实现 operator->*

好在我们有完全等价的第 4 个方法,直接用第 4 个办法即可。但如果你特别无聊,你也可以看我实现一下方法 3。

实现一个带有 *-> 的指针类

在代码中的 U 后面加上这一段。

class Pointer
{
public:
    Pointer(U *u) : _u(u) {}

// 实现一个指针该做的事情 
    U *operator->()
    {
        return _u;
    }

    U &operator*()
    {
        return *_u;
    }
///

// 实现 ->* //
    template <typename T>
    decltype(auto) operator->*(T Y)
    {
        if constexpr (std::is_member_function_pointer_v<T>)
        {
            return [this, Y](auto &&... args) -> decltype(auto) { return (_u->*Y)(std::forward<decltype(args)>(args)...); };
        }
        else
        {
            return _u->*Y;
        }
    }
//
private:
    U *_u;
};

这样使用的时候就可以用 *-> 运算符了,完整的测试代码如下(两个链接的代码相同)

Coliru Viewer​coliru.stacked-crooked.com Code, Compile, Run, Debug online C, C++​onlinegdb.com

核心部分是下面这一段代码:

C++17 引入的 if constexpr 可以让我们根据 Y 所代表的的成员类型,返回一个闭包(如果 Y 指向一个函数)或者直接返回(如果 Y 指向一个非引用的非函数对象)。

闭包里面还用到了可变长参数和完美转发。而且我们借助于 C++14 引入的 decltype(auto) 进行返回值的型别推导。注意,这一点很重要,因为有时候需要返回值,有时候需要返回引用。但是 auto 只能返回值,auto&& 只能返回引用,只有 decltype(auto) 能根据返回类型选择合适的。

这些都是很有用的现代特性。

    template <typename T>
    decltype(auto) operator->*(T Y)
    {
        if constexpr (std::is_member_function_pointer_v<T>)
        {
            return [this, Y](auto &&... args) -> decltype(auto) { return (_u->*Y)(std::forward<decltype(args)>(args)...); };
        }
        else
        {
            return _u->*Y;
        }
    }

尽管如此,我们还是会丢失被转发函数的 noexcept const 属性等,不能说完全的完美(也可以花更大代价去实现)。而且还要多传一次 this 和 Y,总体上还不如第 4 种方法呢。

对现有的智能指针打补丁

声明改成

template <typename T, typename S>
decltype(auto) operator->* (std::shared_ptr<T> ptr,S Y);

里面也一起改改,应该就行了吧。

致谢

若不是 @孟晨 的坚持和指导,我真觉得这个是写不出来的

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值