C++自定义数据结构支持for range和结构化绑定

本文记录C++中如何使自定义对象支持for range和结构化绑定

实现一个支持基于范围的for循环的类

自定义类支持for range的条件是:

  1. 该类型需要有和其类型相关的beginend函数,可以是成员函数也可以是全局函数。
  2. beginend返回的对象需要支持operator*operator!=operator++运算符。
    以下是一个demo:
#include <iostream>

class IntIter
{
  public:
    IntIter(int* p) : p_{p}
    {
    }

    bool operator!=(const IntIter& other)
    {
        return p_ != other.p_;
    }

    const IntIter& operator++()
    {
        p_++;
        return *this;
    }

    int operator*() const
    {
        return *p_;
    }

  private:
    int* p_;
};

template <unsigned int fix_size>
class FixIntVector
{
  public:
    FixIntVector(std::initializer_list<int> init_list)
    {
        int* cur = data_;
        for (auto e : init_list)
        {
            *cur = e;
            cur++;
        }
    }

    IntIter begin()
    {
        return IntIter{data_};
    }

    IntIter end()
    {
        return IntIter{data_ + fix_size};
    }

  private:
    int data_[fix_size]{0};
};

int main()
{
    FixIntVector<10> fix_int_vector{1, 3, 5, 7, 9};
    for (auto e : fix_int_vector)
    {
        std::cout << e << std::endl;
    }
}

编译运行:
在这里插入图片描述
实现了beginend的好处远不止于for range,还可以使用符合条件的STL的函数。
比如std::for_eachstd::reversestd::for_each的使用demo:

int main()
{
    FixIntVector<10> fix_int_vector{1, 3, 5, 7, 9};
    std::for_each(fix_int_vector.begin(), fix_int_vector.end(), [&](int num) { std::cout << num << " "; });
    std::cout << std::endl;

    return 0;
}

执行结果:
在这里插入图片描述
但是上面实现的迭代器还不能使用std::sort,因为std::sort要求迭代器是双向随机迭代器,所以需要拓展满足双向随机迭代器的条件后才能使用。
日常开发中,并不总是需要为自定义类型添加扩展来使用STL的,大部分时候标准库自带的容器就够用了,不明白为啥老是要自己定义一套。能用标准库提供的就用标准库提供的,可以去这个网站先看看标准库有没有提供对应算法,没有再自己写,挺好用的
在这里插入图片描述

结构化绑定

C++17提供了结构化绑定的特性,可以将一个或多个名称绑定到初始化对象中的一个或者多个子对象上。

绑定到原生数组

结构化绑定数组的时候,编译器要能知道原生数组的元素个数
在这里插入图片描述

绑定到结构体和类对象

将标识符绑定到结构体和类的非静态成员变量上要满足以下条件:

  1. 类或者结构体中的非静态数据成员个数必须和标识符列表中的别名的个数相同
  2. 这些数据成员必须是公有的
  3. 数据成员必须是在同一个类或者基类中
  4. 绑定的类和结构体不能存在匿名联合体
    在这里插入图片描述

绑定到元组和类元组的对象

先来看这段代码:


#include <tuple>

int main()
{
    std::tuple<int, double, char> t{1, 2.2, 'c'};
    const auto& [x, y, z] = t;
}

C++ Insights可以看到被处理成:

#include <tuple>

int main()
{
  std::tuple<int, double, char> t = std::tuple<int, double, char>{1, 2.2000000000000002, 'c'};
  const std::tuple<int, double, char> & __t7 = t;
  const int & x = std::get<0UL>(__t7);
  const double & y = std::get<1UL>(__t7);
  const char & z = std::get<2UL>(__t7);
  return 0;
}

对于绑定元组和类元组对象有一系列条件:对于元组或者类元组类型T

  1. 需要满足std::tuple_size<T>::value是一个符合语法的表达式,并且该表达式获得的整数值与标识符列表中的别名个数相同。
  2. 类型T还需要保证std::tuple_element<i, T>::type也是一个符合语法的表达式,其中i是小于std::tuple_size<T>::value的整数,表达式代表了类型T中第i个元素的类型。
  3. 类型T必须存在合法的成员函数模板get<i>()或者函数模板get<i>(t),其中i是小于std::tuple_size<T>::value的整数,t是类型T的实例,get<i>()get<i>(t)返回的是实例t中第i个元素的值。
    下面是一个类元组对象支持结构化绑定的demo:
#include <iostream>
#include <tuple>

class BindBase3
{
  public:
    int a = 42;
};

class BindTest3 : public BindBase3
{
  public:
    double b = 11.7;
};

namespace std
{
template <>
struct tuple_size<BindTest3>
{
    static constexpr size_t value = 2;
};

template <>
struct tuple_element<0, BindTest3>
{
    using type = int;
};

template <>
struct tuple_element<1, BindTest3>
{
    using type = double;
};

}  // namespace std

template <std::size_t Idx>
auto& get(BindTest3& bt) = delete;

template <>
auto& get<0>(BindTest3& bt)
{
    return bt.a;
}

template <>
auto& get<1>(BindTest3& bt)
{
    return bt.b;
}

int main()
{
    BindTest3 bt3;
    auto& [x3, y3] = bt3;
    x3 = 78;
    std::cout << bt3.a << std::endl;
}

编译运行看效果:
在这里插入图片描述

参考资料

《现代C++语言核心特性解析》
https://zhuanlan.zhihu.com/p/158647883
https://hackingcpp.com/cpp/std/container_traversal.html
https://cppinsights.io/

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值