C++Primer第五版 习题答案 第十六章 模板与泛型编程(Templates and Generic Programming)

练习16.1

给出实例化的定义。

实例化:编译器处理过程,用实际的模板实参来生成模板的一个特殊实例,其中参数被替换为对应的实参。当函数模板被调用时,会自动根据传递给它的实参来实例化。而使用类模板时,则需要我们提供显式模板实参。

练习16.2

编写并测试你自己版本的 compare 函数。

#include <iostream>

template<typename T>
int compare(const T &lhs, const T &rhs)
{
   
   
    if(lhs < rhs) return -1;
    if(rhs < lhs) return 1;
    return 0;
}

int main()
{
   
   
    std::cout << compare(1, 2) << std::endl;

    return 0;
}

练习16.3

对两个 Sales_data 对象调用你的 compare 函数,观察编译器在实例化过程中如何处理错误。

error: no match for ‘operator<(operand types are ‘const Sales_data’ and ‘const Sales_data’)

练习16.4

编写行为类似标准库 find 算法的模版。函数需要两个模版类型参数,一个表示函数的迭代器参数,另一个表示值的类型。使用你的函数在一个 vector 和一个list中查找给定值。

#include <iostream>
#include <vector>
#include <list>
#include <string>

class Sales_data{
   
    };

template<typename InputIt, typename T>
InputIt find(const InputIt beg, const InputIt end, const T &value)
{
   
   
    for (InputIt iter = beg; iter != end; ++iter)
    {
   
   
        if (*iter == value)
        {
   
   
            return iter;
        }
    }
}

int main()
{
   
   
    std::vector<int> vi = {
   
   1, 2, 3};
    std::list<std::string> ls = {
   
   "aa", "bb", "cc"};

    std::cout << *(find(vi.begin(), vi.end(), 3)) << std::endl;
    std::cout << *(find(ls.begin(), ls.end(), "bb")) << std::endl;

    return 0;
}

练习16.5

为6.2.4节中的print函数编写模版版本,它接受一个数组的引用,能处理任意大小、任意元素类型的数组。

#include <iostream>
#include <string>

template<typename T>
void print(const T &arr)
{
   
   
    for(auto elem : arr)
        std::cout << elem << std::endl;
}

int main()
{
   
   
    char ac[] = "aabbccdd";
    int ai[] = {
   
   1, 2, 3, 4};

    print(ac);
    print(ai);

    return 0;
}

练习16.6

你认为接受一个数组实参的标准库函数 begin 和 end 是如何工作的?定义你自己版本的 begin 和 end。

#include <iostream>
#include <string>

template<typename T, unsigned N>
T* begin(T (&arr)[N])
{
   
   
    return arr;
}

template<typename T, unsigned N>
T* end(T (&arr)[N])
{
   
   
    return arr + N;
}

int main()
{
   
   
    char ac[] = "aabbccdd";

    std::cout << *(begin(ac)) << std::endl;
    std::cout << *(end(ac) - 1) << std::endl;

    return 0;
}

练习16.7

编写一个 constexpr 模版,返回给定数组的大小。

#include <iostream>
#include <string>

template<typename T, unsigned size>
constexpr unsigned getSize(const T(&)[size])
{
   
   
    return size;
}

int main()
{
   
   
    char ac[] = "aabbccdd";

    std::cout << getSize(ac) << std::endl;

    return 0;
}

练习16.8

在第97页的“关键概念”中,我们注意到,C++程序员喜欢使用 != 而不喜欢 < 。解释这个习惯的原因。

因为!=更符合“编写类型无关的代码”的原则。

练习16.9

什么是函数模版,什么是类模版?

函数模板,模板定义,可从它实例化出特定函数。函数模板的定义以关键字template开始,后跟尖括号对<和>,其内为一个用逗号分隔的一个或多个模板参数列表,随后是函数的定义。
类模板,模板定义,可从它实例化出特定的类。类模板的定义以template开始,后跟尖括号<和>,其内为一个用逗号分隔的一个或多个模板参数的列表,随后是类的定义。与函数模板不同之处是,编译器不能为类模板推断模板参数类型。

练习16.10

当一个类模版被实例化时,会发生什么?

编译器使用显式模板参数列表来实例化特定的类。

练习16.11

下面 List 的定义是错误的。应如何修改它?

template <typename elemType> class ListItem;
template <typename elemType> class List {
   
   
public:
	List<elemType>();
	List<elemType>(const List<elemType> &);
	List<elemType>& operator=(const List<elemType> &);
	~List();
	void insert(ListItem *ptr, elemType value);
private:
	ListItem *front, *end;
};
template <typename elemType> class ListItem;
template <typename elemType> class List
{
   
   
public:
    List<elemType>();
    List<elemType>(const List<elemType> &);
    List<elemType>& operator=(const List<elemType> &);
    ~List();
    void insert(ListItem<elemType> *ptr, elemType value);
private:
    ListItem<elemType> *front, *end;
};

int main()
{
   
   
    return 0;
}

练习16.12

编写你自己版本的 Blob 和 BlobPtr 模版,包含书中未定义的多个const成员。

#ifndef STRBLOB_H_
#define STRBLOB_H_

#include <string>
#include <initializer_list>
#include <memory>
#include <vector>
#include <stdexcept>

template <typename T>
class ConstStrBlobPtr;

template <typename T>
class StrBlob
{
   
   
public:
    friend class ConstStrBlobPtr<T>;

    typedef typename std::vector<T>::size_type size_type;

    StrBlob();
    StrBlob(std::initializer_list<T> il);
    size_type size() const {
   
    return data->size(); }
    bool empty() const {
   
    return data->empty(); }
    void push_back(const T &t) {
   
    data->push_back(t); }
    void pop_back();
    T& front();
    T& back();
    const T& front() const;
    const T& back() const;
    ConstStrBlobPtr<T> begin();
    ConstStrBlobPtr<T> end();
private:
    std::shared_ptr<std::vector<T>> data;
    void check(size_type i, const T &msg) const;
};

template <typename T>
class ConstStrBlobPtr
{
   
   
public:
    ConstStrBlobPtr<T>() : curr(0){
   
   };
    ConstStrBlobPtr<T>(const StrBlob<T> &a, size_t sz = 0) : wptr(a.data), curr(sz) {
   
   }
    T& deref() const;
    ConstStrBlobPtr<T>& incr();
private:
    std::shared_ptr<std::vector<T>> check(std::size_t, const T&) const;
    std::weak_ptr<std::vector<T>> wptr;
    std::size_t curr;
};

template <typename T>
std::shared_ptr<std::vector<T>> ConstStrBlobPtr<T>::check(std::size_t i, const T &msg) const
{
   
   
    auto ret = wptr.lock();
    if(!ret)
        throw std::runtime_error("unbound ConstStrBlobPtr<T>");
    if(i >= ret->size())
        throw std::out_of_range(msg);
    return ret;
}

template <typename T>
T& ConstStrBlobPtr<T>::deref() const
{
   
   
    auto p = check(curr, "dereference past end");
    return (*p)[curr];
}

template <typename T>
ConstStrBlobPtr<T>& ConstStrBlobPtr<T>::incr()
{
   
   
    check(curr, "increment past end of ConstStrBlobPtr<T>");
    ++curr;
    return *this;
}

template <typename T>
StrBlob<T>::StrBlob() : data(std::make_shared<std::vector<T>>()){
   
   }

template <typename T>
StrBlob<T>::StrBlob(std::initializer_list<T> il) : data(std::make_shared<std::vector<T>>(il)){
   
   }

template <typename T>
void StrBlob<T>::check(size_type i, const T &msg) const
{
   
   
    if(i >= data->size())
        throw std::out_of_range(msg);
}

template <typename T>
T & StrBlob<T>::front()
{
   
   
    check(0, "front on empty StrBlob");
    return data->front();
}

template <typename T>
T & StrBlob<T>::back()
{
   
   
    check(0, "back on empty StrBlob");
    return data->back();
}

template <typename T>
const T& StrBlob<T>::front() const
{
   
   
    check(0, "front on empty StrBlob");
    return data->front();
}

template <typename T>
const T& StrBlob<T>::back() const
{
   
   
    check(0, "back on empty StrBlob");
    return data->back();
}

template <typename T>
void StrBlob<T>::pop_back()
{
   
   
    check(0, "pop_back on empty StrBlob");
    data->pop_back();
}

template <typename T>
ConstStrBlobPtr<T> StrBlob<T>::begin() {
   
    return ConstStrBlobPtr<T>(*this); }

template <typename T>
ConstStrBlobPtr<T> StrBlob<T>::end()
{
   
   
    auto ret = ConstStrBlobPtr<T>(*this, data->size());
    return ret;
}

#endif

练习16.13

解释你为 BlobPtr 的相等和关系运算符选择哪种类型的友好关系?

一对一的友好关系,相同类型实例化的BlobPtr可以相互比较。

练习16.14

编写 Screen 类模版,用非类型参数定义 Screen 的高和宽。

Screen.h

#ifndef SCREEN_EX23_H_
#define SCREEN_EX23_H_

#include <string>
#include <vector>

// template <unsigned W, unsigned H>
// class Screen;

// template <unsigned W, unsigned H>
// class Window_mgr
// {
   
   
// public:
//     using ScreenIndex = std::vector<Screen<W,H>>::size_type;
//     void clear(ScreenIndex<W,H>);
// private:
//     std::vector<Screen<W,H>> screens;
// };

template <unsigned W, unsigned H>
class Screen 
{
   
   
// friend void Window_mgr::clear(ScreenIndex);
public:
    using pos = std::string::size_type;

    Screen() = default;
    Screen(char c) : contents(H*W, c){
   
    }

    char get() const {
   
    return contents[cursor]; }
    char get(pos r, pos c) const {
   
    return contents[r*width+c]; }
    Screen &move(pos r, pos c);
    Screen &set(char);
    Screen &set(pos, pos, char);
    Screen &display(std::ostream &os) {
   
   do_display(os); return *this;}
    const Screen &display(std::ostream &os) const {
   
   do_display(os); return *this;}
    // pos size() const;

private:
    pos cursor = 0;
    pos height = H, width = W;
    std::string contents;
    void do_display(std::ostream &os) const {
   
   os << contents;}
};


template <unsigned W, unsigned H>
inline Screen<W, H> &Screen<W, H>::move(pos r, pos c)
{
   
   
    pos row = r * width;
    cursor = row + c;
    return *this;
}

template <unsigned W, unsigned H>
inline Screen<W, H> &Screen<W, H>::set(char c)
{
   
   
    contents[cursor] = c;
    return *this;
}

template <unsigned W, unsigned H>
inline Screen<W, H> &Screen<W, H>::set(pos r, pos col, char c)
{
   
   
    contents[r*width + col] = c;
    return *this;
}

// template <unsigned W, unsigned H>
// Screen<W, H>::pos Screen<W, H>::size() const
// {
   
   
//     return height * width;
// }

// void Window_mgr::clear(ScreenIndex i)
// {
   
   
//     Screen &s = screens[i];
//     s.contents = std::string(s.height * s.width, ' ');
// }

#endif

ex14.cpp

#include <string>
#include <iostream>
#include "Screen.h"

int main()
{
   
   
    Screen<5, 5> myScreen('X');
    
    myScreen.move(4, 0).set('#').display(std::cout);
    std::cout << "\n";
    myScreen.display(std::cout);
    std::cout << "\n";
    // std::cout << myScreen.size() << std::endl;

    return 0;
}

练习16.15

为你的 Screen 模版实现输入和输出运算符。Screen 类需要哪些友元(如果需要的话)来令输入和输出运算符正确工作?解释每个友元声明(如果有的话)为什么是必要的。

Screen.h

#ifndef SCREEN_EX23_H_
#define SCREEN_EX23_H_

#include <string>
#include <vector>

// template <unsigned W, unsigned H>
// class Screen;

// template <unsigned W, unsigned H>
// class Window_mgr
// {
   
   
// public:
//     using ScreenIndex = std::vector<Screen<W,H>>::size_type;
//     void clear(ScreenIndex<W,H>);
// private:
//     std::vector<Screen<W,H>> screens;
// };

template <unsigned W, unsigned H>
class Screen 
{
   
   
// friend void Window_mgr::clear(ScreenIndex);
public:
    using pos = std::string::size_type;

    friend std::ostream &operator<<(std::ostream &os, const Screen<H, W> &c)
    {
   
   
        unsigned int i, j;
        for(i = 0; i < c.height; ++i)
        {
   
   
            os << c.contents.substr(i * W, W) << std::endl;
        }
        return os;
    }

    friend std::istream &operator>>(std::istream &is, Screen &c)
    {
   
   
        char a;
        is >> a;
        std::string tmp(H * W, a);
        c.contents = tmp;
        return is;
    }

    Screen() = default;
    Screen(char c) : contents(H*W, c){
   
    }

    char get() const {
   
    return contents[cursor]; }
    char get(pos r, pos c) const {
   
    return contents[r*width+c]; }
    Screen &move(pos r, pos c);
    Screen &set(char);
    Screen &set(pos, pos, char);
    Screen &display(std::ostream &os) {
   
   do_display(os); return *this;}
    const Screen &display(std::ostream &os) const {
   
   do_display(os); return *this;}
    // pos size() const;

private:
    pos cursor = 0;
    pos height = H, width = W;
    std::string contents;
    void do_display(std::ostream &os) const {
   
   os << contents;}
};


template <unsigned W, unsigned H>
inline Screen<W, H> &Screen<W, H>::move(pos r, pos c)
{
   
   
    pos row = r 
评论 6
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值