泛型程序设计实例(一)

来源:互联网 发布:新晨科技 知乎 编辑:程序博客网 时间:2024/05/24 05:03

  • 问题提出
  • 直接思维
  • 泛型思维
  • 作死思维
  • 瞬间爆炸


问题提出

假如我有三种矩阵,3行3列,1行9列,9行1列,我希望在输出的时候

  • 3行3列
0 0 00 0 00 0 0
  • 1行9列
0 0 0 0 0 0 0 0 0
  • 9行1列
000000000

直接思维

最直接的设计应该是这样子的:

// m_model 表示哪一种矩阵// Square 代表3行3列// Horizontal 代表1行9列// Vertical 代表9行1列// m_numbers 表示内部数组void Matrix::showMatrix() const {    switch(this->m_model) {        case MatrixModel::Square: {            for(auto index = 0; index != 9; ++index) {                std::cout << this->m_numbers[index] << ((index+1)%3==0 ? '\n':' ');            }            std::cout << std::endl;            break;        }        case MatrixModel::Horizontal: {            for(auto index = 0; index != 9; ++index) {                std::cout << this->m_numbers[index] << ' ';            }            std::cout << std::endl;            break;        }        case MatrixModel::Vertical: {            for(auto index = 0; index != 9; ++index) {                std::cout << this->m_numbers[index] << '\n';            }            std::cout << std::endl;            break;        }        default: {            break;        }    }}

泛型思维

观察以上代码,发现语义上有重复的地方:

// 遍历for(auto index = 0; index != 9; ++index) {    // 输出    ...    // 条件输出    // 结束换行}

从代码上直观的看,只有一部分不相同:

// << ((index+1)%3==0 ? '\n':' ');// << ' ';// << '\n';

那么我们将这三个部分单独包装起来,什么!你不知道用什么包装?就地闭包啊!

// 对于第一个:[] () {    static int index = 1;    return ((index++)%3==0 ? '\n':' ');}// 对于第二个:[] () {    return ' ';}// 对于第三个:[] () {    return '\n';}

剩下三个都一样的部分,还是就地闭包:

auto output = [this]() {    for(auto i : this->m_numbers) {        std::cout << i;    }    std::cout << std::endl;};

然后怎么把这两个东西用到一起,借助 std::function:

auto output = [this](std::function<char(void)> func) {    for(auto i : this->m_numbers) {        std::cout << i << func();    }    std::cout << std::endl;};

最后组合到一起:

void Matrix::showMatrix() const {    auto output = [this](std::function<char(void)> func) {        for(auto i : this->m_numbers) {            std::cout << i << func();        }        std::cout << std::endl;    };    switch(this->m_model) {        case MatrixModel::Square: {            output([](){                static int index = 1;                return ((index++)%3==0 ? '\n':' ');            });            break;        }        case MatrixModel::Horizontal: {            output([](){                return ' ';            });            break;        }        case MatrixModel::Vertical: {            output([](){                return '\n';            });            break;        }        default: {            break;        }    }}

作死思维

然后我们注意到,switch case 的影响结果其实和 output 闭包没有一点关系,它的结果只影响三个子闭包,所以我们是不是可以考虑只编译一次 output 闭包?

那就把 switch case 和它有关的部分绑定到一起:

int index = 1;auto select = [this,&index](){    switch(this->m_model) {        case MatrixModel::Square: {            return ((index++)%3==0 ? '\n':' ');        }        case MatrixModel::Horizontal: {            return ' ';        }        case MatrixModel::Vertical: {            return '\n';        }        default: {            break;        }    }};output(select);

这样子,看上去好晦涩的写法:),其实也就是俩闭包:

void Matrix::showMatrix() const {    auto output = [this](std::function<char(void)> func) {        for(auto i : this->m_numbers) {            std::cout << i << func();        }        std::cout << std::endl;    };    int index = 1;    auto select = [this,&index](){        switch(this->m_model) {            case MatrixModel::Square: {                return ((index++)%3==0 ? '\n':' ');            }            case MatrixModel::Horizontal: {                return ' ';            }            case MatrixModel::Vertical: {                return '\n';            }            default: {                break;            }        }    };    output(select);}

瞬间爆炸

是否还记得模板参数推导和模板特化,我试图将 switch 的过程移动到编译期,也就是说在运行期就不需要再 switch 考虑了

// 首先定义主模板template <MatrixModel model>void Matrix::show() const {    std::cout << "None" << std::endl;}// 特化为 Square 的情况template <>void Matrix::show<MatrixModel::Square>() const {    std::cout << "Square" << std::endl;    int index = 0;    for(auto i : this->m_numbers) {        std::cout << i << ((index++)%3==0 ? '\n':' ');    }    std::cout << std::endl;}// 特化为 Horizontal 的情况template <>void Matrix::show<MatrixModel::Horizontal>() const {    std::cout << "Horizontal" << std::endl;    for(auto i : this->m_numbers) {        std::cout << i << ' ';    }    std::cout << std::endl;}// 特化为 Vertical 的情况template <>void Matrix::show<MatrixModel::Vertical>() const {    std::cout << "Vertical" << std::endl;    for(auto i : this->m_numbers) {        std::cout << i << '\n';    }    std::cout << std::endl;}

然而这样子问题就来了,导致了在编译期由我们决定要按何种形式显示:

matrix.show<MatrixModel::Square>();matrix.show<MatrixModel::Horizontal>();matrix.show<MatrixModel::Vertical>();

对此,我只能说:WTF!到此为止!


CSDN 辣鸡 MD 编辑器,无序列表格式全丢

原创粉丝点击