条款6:auto推导若非己愿,使用显式类型初始化惯用法

news/2024/12/25 1:52:27 标签: c++, 开发语言

一、代理类

所谓的代理类就是以模仿和增强一些类型的行为为目的存在的类

class MyArray
{
public:
    class MyArraySize
    {
    public:
        MyArraySize(int size) : theSize(size) {}
        int size() const { return theSize; }
        operator int() const { return theSize; }

    private:
        int theSize;
    };

    MyArray(MyArraySize size) : size_(size), data_(new int[size.size()]) {}
    int operator[](int index)
    {
        return data_[index];
    }
    bool operator==(const MyArray &temp)
    {
        return data_ == temp.data_;
    }
    MyArraySize size() { return size_; }

private:
    int *data_;
    MyArraySize size_;
};

class MyArray_
{
public:
    // C++ 11之前没有explicit
    MyArray_(int size) : size_(size), data_(new int[size]) {}

private:
    int *data_;
    int size_;
};

void func1(MyArray arr)
{
}

void func2(MyArray_ arr)
{
}

int main()
{
    func2(10);
    func1(10);  // 错误,不能进行两次类型转换
    func1(MyArray(10)); // 通过代理类,可以避免上面隐式转换的情况
    return 0;


    myArray arr(10);
    auto size = arr.size();  // auto 推导为MyArray::MyArraySize
    int size_ = arr.size();  // 因为存在operator int()隐式类型转换
}

二、临时变量的引用不要用引用来接

#include <vector>
#include <iostream>

class A
{
public:
    A() { std::cout << "A::A()" << std::endl; }
    A(int data) : _data(data) { std::cout << "A::A(int data)" << std::endl; }
    A(const A &a) : _data(a._data)
    {
        std::cout << "A::A(const A &a)" << std::endl;
    }
    A(A &&a) : _data(a._data)
    {
        std::cout << "A::A(A &&a)" << std::endl;
    }
    ~A() { std::cout << "A::~A()" << std::endl; }

    int _data{100};
};

std::vector<A> func()
{
    return {A(1), A(2), A(3), A(4), A(5)};
}

int main()
{
    A aaa = func()[2];    // 发生拷贝
    A &bbb = func()[3];   // 错误,引用了一个临时变量

    return 0;
}
#include <vector>
#include <iostream>

std::vector<bool> features()
{
    return {true, true, true, false, false};
}

int main()
{
    bool highPriority = features()[4];
    auto highPriority = features()[4]; // 错误写法,推导为引用类型的代理类
        // auto推到为std::vector<bool>::reference
        // reference在vector中其实是一个代理类
    auto highPriority_ = static_cast<bool>(features()[4]);

    return 0;
}

三、expressin template

#include <cassert>
#include <iostream>
#include <vector>

template <typename Derived>
void func(Base<Derived> derived)
{
    derived.name();
}

// https://blog.csdn.net/HaoBBNuanMM/article/details/109740504
// CRTP中的基类模板
template <typename E>
class VecExpression
{
public:
    // 通过将自己static_cast成为子类,调用子类的对应函数实现实现静态多态
    double operator[](size_t i) const { return static_cast<E const &>(*this)[i]; }
    size_t size() const { return static_cast<E const &>(*this).size(); }
};

// 将自己作为基类模板参数的子类 - 对应表达式编译树中的叶节点
class Vec : public VecExpression<Vec>
{
    std::vector<double> elems;

public:
    double operator[](size_t i) const { return elems[i]; }
    double &operator[](size_t i) { return elems[i]; }
    size_t size() const { return elems.size(); }

    Vec(size_t n) : elems(n) {}

    Vec(std::initializer_list<double> init)
    {
        for (auto i : init)
            elems.push_back(i);
    }

    // 赋值构造函数可以接受任意父类VecExpression的实例,并且进行表达式的展开
    // (对应表达式编译树中的赋值运算符节点)
    template <typename E>
    Vec(VecExpression<E> const &vec) : elems(vec.size())
    {
        for (size_t i = 0; i != vec.size(); ++i)
        {
            elems[i] = vec[i];
        }
    }
};

// 将自己作为基类模板参数的子类 - 对应表达式编译树中的二元运算符输出的内部节点
// 该结构的巧妙之处在于模板参数E1 E2可以是VecSum,从而形成VecSum<VecSum<VecSum ... > > >的嵌套结构,体现了表达式模板的精髓:将表达式计算改造成为了构造嵌套结构
template <typename E1, typename E2>
class VecSum : public VecExpression<VecSum<E1, E2>>
{
    E1 const &_u;
    E2 const &_v;

public:
    VecSum(E1 const &u, E2 const &v) : _u(u), _v(v)
    {
        assert(u.size() == v.size());
    }

    double operator[](size_t i) const { return _u[i] + _v[i]; }
    size_t size() const { return _v.size(); }
};

// 对应编译树上的二元运算符,将加法表达式构造为VecSum<VecSum... > >的嵌套结构
template <typename E1, typename E2>
VecSum<E1, E2> const operator+(E1 const &u, E2 const &v)
{
    return VecSum<E1, E2>(u, v);
}

// 主函数入口
int main()
{
    // 创建3个叶子节点
    Vec v0 = {1.0, 1.0, 1.0}; 
    Vec v1 = {2.0, 2.0, 2.0}; 
    Vec v2 = {3.0, 3.0, 3.0}; 
    auto v4 = v0 + v1;  //auto 推导为VecSum<Vec, Vec> v4,这也是一个代理类

    // 如果想要的不是这种类型
    // 解决方案1
    Vec v4 = v0 + v1;
    // 解决方案2
    auto v4 =static_cast<Vec>( v0 + v1);
    
    return 0;
}


http://www.niftyadmin.cn/n/5798402.html

相关文章

微服务篇-深入了解 MinIO 文件服务器(你还在使用阿里云 0SS 对象存储图片服务?教你使用 MinIO 文件服务器:实现从部署到具体使用)

&#x1f525;博客主页&#xff1a; 【小扳_-CSDN博客】 ❤感谢大家点赞&#x1f44d;收藏⭐评论✍ 文章目录 1.0 MinIO 文件服务器概述 1.1 MinIO 使用 Docker 部署 1.2 MinIO 控制台的使用 2.0 使用 Java 操作 MinIO 3.0 使用 minioClient 对象的方法 3.1 判断桶是否存在 3.2…

Docker环境下MySQL数据库持久化部署全攻略

概述 在当今的软件开发领域&#xff0c;Docker容器技术已经成为应用部署和管理的新标准。它不仅简化了应用的部署流程&#xff0c;还为数据管理提供了灵活的解决方案。特别是在涉及到MySQL数据库时&#xff0c;数据持久化是一个不可忽视的重要环节。本文将分享如何在Docker中部…

Kafka常用集群配置参数

Broker log.dirs 这是非常重要的参数&#xff0c;指定了 Broker 需要使用的若干个文件目录路径。比如/home/kafka1,/home/kafka2,/home/kafka3这样 log.dirs /home/kafka1,/home/kafka2,/home/kafka3如果有条件的话你最好保证这些目录挂载到不同的物理磁盘上。 1、可以提升…

Linux系统的阻塞方式和非阻塞方式是什么意思?

在Linux系统中&#xff0c;阻塞方式和非阻塞方式是描述进程&#xff08;或线程&#xff09;在执行某些操作&#xff08;如I/O操作&#xff09;时的行为模式。这些模式影响了进程的运行状态以及如何处理资源等待。以下是详细解释&#xff1a; 阻塞方式 阻塞方式是指进程在执行某…

重温设计模式--模板方法模式

文章目录 一、模板方法模式概述二、模板方法模式UML图三、优点1代码复用性高2可维护性好3扩展性强 四、缺点五、使用场景六、C 代码示例1七、 C 代码示例2 一、模板方法模式概述 定义&#xff1a;定义一个操作中的算法骨架&#xff0c;而降一些步骤延迟到子类中。模板方法使得…

R9000P键盘失灵解决办法

问题描述 突然&#xff0c;就是很突然&#xff0c;我买的R9000P 2024不到三个月&#xff0c;键盘突然都不能用了&#xff0c;是所有键盘按键都无效的那种。&#xff08;可以使用外接键盘&#xff09; 解决办法 我本科室友说的好哈&#xff0c;全坏全没坏。 &#xff08;该解…

ssr实现方案

目录 序言 一、流程 二、前端要做的事情 三、节点介绍 四、总结 序言 本文不是详细的实现过程&#xff0c;是让你最快最直接的理解ssr的真正实现方法&#xff0c;有前端经验的同学&#xff0c;能够很好的理解过程&#xff0c;细节根据具体项目实现 一、前端要做的事情 1.…

AWTK-WEB 快速入门(2) - JS 应用程序

AWTK 可以使用相同的技术栈开发各种平台的应用程序。有时我们需要使用 Web 界面与设备进行交互&#xff0c;本文介绍一下如何使用 JS 语言开发 AWTK-WEB 应用程序。 用 AWTK Designer 新建一个应用程序 先安装 AWTK Designer&#xff1a; https://awtk.zlg.cn/web/index.html…