C++ Primer课后习题解答(第十六章)

Exercises Section 16.1.1

Ex16.1

编译器用推断出的模板参数生成一个特定版本的函数,这个过程就叫做实例化。

Ex16.2

#include<iostream>
#include<functional>

using namespace std;

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

int main()
{
    int a, b;
    cin >> a >> b;
    cout << compare(a, b) << endl;
    system("pause");
    return 0;
}

Ex16.3

如果你的 compare 函数使用的 < 操作符,那么实例化 Sales_data 会发生错误,因为 Sales_data 未定义 < 操作符。

Ex16.4

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

using namespace std;

template <typename T, typename U>
T find(T beg, T end, U val)
{
    auto iter = beg;
    while (iter != end)
    {
        if (*iter == val)
            break;
        ++iter;
    }
    return iter;
}

int main()
{
    vector<int> vec = {1, 2, 3, 4, 5, 6};
    list<string> ls = {"hello", "world", "hi"};
    int value;
    string word;
    cin >> value >> word;
    auto p1 = find(vec.begin(), vec.end(), value);
    if (p1 != vec.end())
        cout << *p1 << endl;
    auto p2 = find(ls.begin(), ls.end(), word);
    if (p2 != ls.end())
        cout << *p2 << endl;
    system("pause");
    return 0;
}

Ex16.5

#include<iostream>

using namespace std;

template<typename T, unsigned N> void print(const T (&arr)[N])
{
    for (auto v : arr)
        cout << v << " ";
}

int main()
{
    int a[] = {1, 2, 3, 4, 5};
    string b[] = {"hello", "world"};
    print(a);
    cout << endl;
    print(b);
    cout << endl;
    system("pause");
    return 0;
}

Ex16.6

#include<iostream>

using namespace std;

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

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

int main()
{
    int a[] = {1, 2, 3, 4, 5};
    string b[] = {"hello", "world"};
    for (auto p = begin(a); p != end(a); ++p)
        cout << *p << " ";
    cout << endl;
    for (auto p = begin(b); p != end(b); ++p)
        cout << *p << " ";
    cout << endl;
    system("pause");
    return 0;
}

Ex16.7

#include<iostream>

using namespace std;

template<typename T, unsigned N> constexpr unsigned arr_size(const T (&arr)[N])
{
    return N;
}

int main()
{
    int a[] = {1, 2, 3, 4, 5};
    string b[] = {"hello", "world"};
    cout << arr_size(a) << endl;
    cout << arr_size(b) << endl;
    system("pause");
    return 0;
}

Ex16.8

支持 != 操作的类比支持 < 操作的类更多,简而言之,就是 != 操作更通用。

Exercises Section 16.1.2

Ex16.9

函数模板:编译器能够根据推断出的模板参数类型实例化一个特定类型的函数。
类模板:编译器不能推断出模板参数类型,而是需要在<>内的模板名字后面补充信息,然后实例化一个特定类型的类。

Ex16.10

当实例化一个类模板时,编译器重写该类模板,通过给定的类型参数取代所有出现模板参数的地方。

Ex16.11

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;      
};

Ex16.12

template <typename> class BlobPtr;
template <typename T> bool operator==(const Blob<T> &, const Blob<T> &);
template <typename T> bool operator!=(const Blob<T> &, const Blob<T> &);

template <typename T> class Blob
{
    friend class BlobPtr<T>;
    friend bool operator==<T>(const Blob<T> &, const Blob<T> &);
    friend bool operator!=<T>(const Blob<T> &, const Blob<T> &);
public:
    typedef T value_type;
    typedef typename std::vector<T>::size_type size_type;
    Blob();
    Blob(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 push_back(T &&t) { data->push_back(std::move(t)); }
    void pop_back();
    T& back();
    const T& back() const;
    T& operator[](size_type i);
    const T& operator[](size_type i) const;
private:
    std::shared_ptr<std::vector<T>> data;
    void check(size_type i, const std::string &msg) const;
};

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

template <typename T> T& Blob<T>::back()
{
    check(0, "back on empty Blob");
    return data->back();
}
template <typename T> const T& Blob<T>::back() const
{
    check(0, "back on empty Blob");
    return data->back();
}
template <typename T> T& Blob<T>::operator[](size_type i)
{
    check(i, "subscript out of range");
    return (*data)[i];
}

template <typename T> T& Blob<T>::operator[](size_t i) const
{
    check(i, "subscript out of range");
    return (*data)[i];
}

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

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

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

template <typename T> bool operator==(const Blob<T> &lhs, const Blob<T> &rhs)
{
    if (lhs.size() != rhs.size())
        return false;
    for (size_t i = 0; i != lhs.size(); ++i)
    {
        if (lhs[i] != rhs[i])
            return false;
    }
    return true;
}

template <typename T> bool operator!=(const Blob<T> &lhs, const Blob<T> &rhs)
{
    return !(lhs == rhs);
}
template <typename T> bool operator==(const BlobPtr<T> &, const BlobPtr<T> &);
template <typename T> bool operator!=(const BlobPtr<T> &, const BlobPtr<T> &);

template <typename T> class BlobPtr
{
public:
    BlobPtr(): curr(0) { }
    BlobPtr(Blob<T> &a, size_t sz = 0): wptr(a.data), curr(sz) { }
    T& operator*() const
    {
        auto p = check(curr, "dereference past end");
        return (*p)[curr];
    }
    BlobPtr & operator++();
    BlobPtr & operator--();
    BlobPtr operator++(int);
    BlobPtr operator--(int);
private:
    std::shared_ptr<std::vector<T>> check(std::size_t, const std::string &) const;
    std::weak_ptr<std::vector<T>> wptr;
    std::size_t curr;
};

template <typename T> BlobPtr<T> BlobPtr<T>::operator++(int)
{
    BlobPtr ret = *this;
    ++*this;
    return ret;
}

template <typename T> BlobPtr<T> BlobPtr<T>::operator--(int)
{
    BlobPtr ret = *this;
    --*this;
    return ret;
}

template <typename T> bool operator==(const BlobPtr<T> &lhs, const BlobPtr<T> &rhs)
{
    return lhs.wptr.lock().get() == rhs.wptr.lock().get() && lhs.curr == rhs.curr;
}

template <typename T> bool operator!=(const BlobPtr<T> &lhs, const BlobPtr<T> &rhs)
{
    return !(lhs == rhs);
}

Ex16.13

由于函数模板的实例化只处理特定类型,因此对于相等和关系运算符,对每个 BlobPtr 实例和用相同类型实例化的运算符建议一对一的友好关系即可。

Ex16.14

template <int H, int W> class Screen
{
public:
    Screen(): contents(H * W, ' ') { }
    Screen(char c): contents(H * W, c) { }
    char get() const { return contents[cursor]; }
    inline char get(int, int) const;
    Screen &move(int, int);
    Screen &set(char);
    Screen &display(ostream &os)
    {
        do_display(os);
        return *this;
    }
    const Screen &display(ostream &os) const
    {
        do_display(os);
        return *this;
    }
private:
    int cursor = 0;
    string contents;
    void do_display(ostream &os) const { os << contents; }
};

template <int H, int W> inline char Screen<H, W>::get(int r, int c) const
{
    int row = r * W;
    return contents[row + c];
}

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

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

Ex16.15

template <int H, int W> istream & operator>>(istream &, Screen<H, W> &);
template <int H, int W> ostream & operator<<(ostream &, Screen<H, W> &);

template <int H, int W> class Screen
{
    friend istream & operator>><H, W>(istream &, Screen<H, W> &);
    friend ostream & operator<<<H, W>(ostream &, Screen<H, W> &);
    // 其他成员不变
};

template <int H, int W> istream & operator>>(istream &is, Screen<H, W> &sc)
{
    string s;
    is >> s;
    sc.contents = s.substr(0, H * W);
}

template <int H, int W> ostream & operator<<(ostream &os, Screen<H, W> &sc)
{
    os << sc.contents;
    return os;
}

Ex16.16

template <typename T> class Vec 
{
public:
	Vec(): elements(nullptr), first_free(nullptr), cap(nullptr) { }
	Vec(const Vec&);
	Vec(const T*, const T*);
	Vec(initializer_list<T> lst);
	Vec& operator=(const Vec&);
	~Vec();
	void push_back(const T&);
	size_t size() const { return first_free - elements; }
	size_t capacity() const { return cap - elements; }
	T *begin() const { return elements; }
	T *end() const { return first_free;}
	void reserve(size_t n) { if (n > capacity()) resize(n); }
	void resize(size_t);
	T& operator[](size_t n) { return elements[n]; }
private:
	static allocator<T> alloc;
	void chk_n_alloc() { if (size() == capacity() ) reallocate(); } 
	pair<T*, T*> alloc_n_copy(const T*, const T*);
	void free();
	void reallocate();
	T *elements;
	T *first_free;
	T *cap;	
};
 
template <typename T> std::allocator<T> Vec<T>::alloc;  
 
template <typename T> void Vec<T>::push_back(const T &s)
{
	chk_n_alloc();
	alloc.construct(first_free++, s);
}
 
template <typename T> pair<T*, T*> Vec<T>::alloc_n_copy(const T *b, const T *e)
{
	T *data = alloc.allocate(e - b);
	return make_pair(data, uninitialized_copy(b, e, data));
}
 
template <typename T> void Vec<T>::free()
{
	if (elements) {
		for (auto p = first_free; p != elements; )
			alloc.destroy(--p);
		alloc.deallocate(elements, cap - elements);
	}
}
 
template <typename T> Vec<T>::Vec(const Vec<T> &s)
{
	auto newdata = alloc_n_copy(s.begin(), s.end());
	elements = newdata.first;
	first_free = newdata.second;
	cap = newdata.second;
}
 
template <typename T> Vec<T>::Vec(const T *b, const T *e)
{
	pair<T*, T*> newdata = alloc_n_copy(b, e);
	elements = newdata.first;
	first_free = cap = newdata.second;
}
 
template <typename T> Vec<T>::Vec(initializer_list<T> lst)
{
	auto newdata = alloc_n_copy(lst.begin(), lst.end());
	elements = newdata.first;
	first_free = cap = newdata.second;
}
 
template <typename T> Vec<T>::~Vec() { free(); }
 
template <typename T> Vec<T> &Vec<T>::operator=(const Vec &rhs)
{
	auto newdata = alloc_n_copy(rhs.begin(), rhs.end());
	free();
	elements = newdata.first;
	first_free = newdata.second;
	cap = newdata.second;
	return *this;
}
 
template <typename T> void Vec<T>::reallocate()
{
	auto newcapacity = size() ? size() * 2 : 1;
	auto newdata = alloc.allocate(newcapacity);
	auto dest = newdata;
	auto elem = elements;
	for (size_t i = 0; i != size(); ++i)
		alloc.construct(dest++, std::move(*elem++));
	free();
	elements = newdata;
	first_free = dest;
	cap = elements + newcapacity;
}
 
template <typename T>
void Vec<T>::resize(size_t n)
{
	if (n > size()) 
	{
		while (n > size())
			push_back("");
	}
	else if (n < size())
	{
		while (size() > n)
			alloc.destroy(--first_free);
	}
		
}

Exercises Section 16.1.3

Ex16.17

在模板参数列表中,typename 和 class 含义相同,typename 比 class 更直观,因为内置类型也可以作为模板类型参数。当使用作用域运算符:: 访问一个模板类型参数的成员时,该成员可能为类型或者静态成员,为了区分这点,规定使用 typename 指明其是一个类型。

Ex16.18

a) template <typename T, U, typename V> void f1(T, U, V);   // 非法,U是类型参数,必须有关键字 typename
// 修改如下:
template <typename T, typename U, typename V> void f1(T, U, V);

b) template <typename T> T f2(int &T);      // 非法,T不能作为参数名
// 修改如下:
template <typename T> T f2(T &);

c) inline template <typename T> T foo(T, unsigned int*);    // 非法,inline 位置错误
// 修改如下:
template <typename T> inline T foo(T, unsigned int*);

d) template <typename T> f4(T, T);      // 非法,没有返回类型
// 修改如下:
template <typename T> T f4(T, T);

e) typedef char Ctype;
    template <typename Ctype> Ctype f5(Ctype a);    // 合法,但是模板声明的 Ctype 隐藏了类型别名 Ctype

Ex16.19

#include<iostream>
#include<vector>

using namespace std;

template  <typename T> void print(const T &container)
{
    for (typename T::size_type i = 0; i != container.size(); ++i)
        cout << container.at(i) << endl;
}

int main()
{
    vector<int> vec = {1, 2, 3, 4, 5};
    print(vec);
    system("pause");
    return 0;
}

Ex16.20

#include<iostream>
#include<vector>

using namespace std;

template  <typename T> void print(const T &container)
{
    for (auto it = container.begin(); it != container.end(); ++it)
        cout <<  *it << endl;
}

int main()
{
    vector<int> vec = {1, 2, 3, 4, 5};
    print(vec);
    system("pause");
    return 0;
}

Exercises Section 16.1.4

Ex16.21

class DebugDelete
{
public:
    DebugDelete(std::ostream &s = std::cerr): os(s) { }
    template <typename T> void operator() (T *p) const
    { os << "deleting unique_ptr" << std::endl; delete p; }
private:
    std::ostream &os;
};

Ex16.22

class TextQuery
{
    using line_no = vector<string>::size_type;
public:
    TextQuery(ifstream &in);
    // 其他成员不变
};

TextQuery::TextQuery(ifstream &in): file(new vector<string>, DebugDelete())
{
    //...  
}

Ex16.23

在程序结束时释放 shared_ptr 指向的资源,会在程序结束时看到打印语句。

Ex16.24

template <typename T> class Blob
{
public:
    template <typename It> Blob(It b, It e);
    // 其他成员不变
};

template <typename T> template <typename It> Blob<T>::Blob(It b, It e): data(make_shared<vector<T>>(b, e)) { }

Exercises Section 16.1.5

Ex16.25

extern template class vector<string>;	// extern 表明不在本文件中生成实例化代码,该实例化的定义会在其他文中中。
template class vector<Sales_data>;		// 用 Sales_data 类实例化作为 vector 元素类型,在其他文件中可用 extern 声明此实例化,使用该定义。

Ex16.26

不可以; 当显式实例化 vector 时,编译器会实例化 vector 的所有成员函数,vector 的构造函数会使用元素类型的默认构造函数来对元素进行值初始化,而 Nodefault 没有默认构造函数。

Ex16.27

template <typename T> class Stack { };
void f1(Stack<char>);       // (a)
class Exercise
{
    Stack<double> &rsd;     // (b)
    Stack<int> si;          // (c)
};
int main()
{
    Stack<char> *sc;        // (d)
    f1(*sc);                // (e)
    int iObj = sizeof(Stack<string>);   // (f)
}

(a) 、(b)、(c)、(f)分别进行了 Stack 对 char、double、int、string 类型的实例化,因为这些语句都需要用到其实例化的类;(d)和 (e)未发生实例化,因为在上述对应代码前就已经发生了实例化。

Exercises Section 16.2.1

Ex16.32

在模板参数推断阶段,编译器使用函数调用过程中的参数类型推断模板参数,找出最为匹配给定的函数调用的函数版本。

Ex16.33

  1. 可以将一个 非 const 对象的引用或指针传递给一个 const 的引用或指针
  2. 数组或者函数指针转换;函数形参不是引用类型,则可对数组或函数类型的实参应用正常的指针转换

Ex16.34

都不合法,当模板函数形参为引用类型,数组不能转换为指针。

Ex16.35

template <typename T> T calc(T, int);
template <typename T> fcn(T, T);
double d;   float f;    char c;
a) calc(c, 'c');    // 合法,T 为 char
b) calc(d, f);      // 合法,T 为 double
c) fcn(c, 'c');     // 合法,T 为 char
d) fcn(d, f);       // 非法,无法实例化 fcn(double, float)

Ex16.36

template <typename T> f1(T, T);
template <typename T1, typename T2> f2(T1, T2);
int i = 0, j = 42, *p1 = &i, *p2 = &j;
const int *cp1 = &i, *cp2 = &j;
a) f1(p1, p2);      // T 为 int * 
b) f2(p1, p2);      // T1 为 int *,T2为 int *
c) f1(cp1, cp2);    // T 为 const int *
d) f2(cp1, cp2);    // T1 为 const int *,T2 为 const int *
e) f1(p1, cp1);     // T 为 int *
f) f2(p1, cp1);     // T1 为 int *,T2 为 const int *

Exercises Section 16.2.2

Ex16.37

可以;不过需要显式指定参数类型,如max<double>

Ex16.38

使用 make_shared 时,编译器无法从已有参数推断模板参数类型,这时就要显式指定模板参数类型。

Ex16.39

compare<string>("s1", "s2");
posted @   astralcon  阅读(32)  评论(0编辑  收藏  举报
编辑推荐:
· AI与.NET技术实操系列:基于图像分类模型对图像进行分类
· go语言实现终端里的倒计时
· 如何编写易于单元测试的代码
· 10年+ .NET Coder 心语,封装的思维:从隐藏、稳定开始理解其本质意义
· .NET Core 中如何实现缓存的预热?
阅读排行:
· 25岁的心里话
· 闲置电脑爆改个人服务器(超详细) #公网映射 #Vmware虚拟网络编辑器
· 零经验选手,Compose 一天开发一款小游戏!
· 因为Apifox不支持离线,我果断选择了Apipost!
· 通过 API 将Deepseek响应流式内容输出到前端
点击右上角即可分享
微信分享提示