cppPrimer学习16th
Posted zongzi10010
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了cppPrimer学习16th相关的知识,希望对你有一定的参考价值。
cppPrimer学习16th
目录
- cppPrimer学习16th
- TODO
- 16.1
- 16.2
- 16.3
- 16.4
- 16.5
- 16.6
- 16.7
- 16.8
- 16.9
- 16.10
- 16.11
- 16.12
- 16.13
- 16.14
- 16.15
- 16.17
- 16.18
- 16.19
- 16.20
- 16.21
- 16.24
- 16.25
- 16.26
- 16.27
- 16.28
- 16.29
- 16.30
- 16.31
- 16.32
- 16.33
- 16.34
- 13.35
- 16.36
- 16.37
- 16.38
- 16.39
- 16.40
- 16.41
- 16.42
- 16.43
- 16.44
- 16.45
- 16.46
- 16.47
- 16.48
- 16.49
- 16.50
- 16.51
- 16.52
- 16.53
- 16.54
- 16.55
- 16.56
- 16.57
- 16.58
- 16.59
- 16.60
- 16.61
- 16.62
- 16.63
- 16.64
- 16.65
- 16.66
- 16.67
TODO
16.22
16.23
16.1
给出实例化的定义
我们在调用模块的时候,传递确定的参数,编译器为我们构造真实的函数或者对象
16.2
// 16.2 编写你自己的compare版本
#include <iostream>
template <typename T>
int compare(const T &a, const T &b)
{
if (a > b)
return 1;
if (a < b)
return -1;
return 0;
}
int main(int argc, char const *argv[])
{
std::cout << compare(1, 2) << std::endl;
while (1)
;
return 0;
}
16.3
提示未定义 operator<
error: no match for ‘operator>‘ (operand types are ‘const Sales_data‘ and ‘const Sales_data‘)
// 16.2 编写你自己的compare版本
#include <iostream>
class Sales_data
{
private:
int a;
public:
Sales_data() : a(0) {}
~Sales_data() {}
};
template <typename T>
int compare(const T &a, const T &b)
{
if (a > b)
return 1;
if (a < b)
return -1;
return 0;
}
int main(int argc, char const *argv[])
{
Sales_data a;
Sales_data b;
std::cout << compare(1, 2) << std::endl;
std::cout << compare(a, b) << std::endl;
while (1)
;
return 0;
}
16.4
// 16.4 编写行为类似标准库find算法的模版.函数需要两个模版类型参数,一个表示函数的迭代器参数,另一个表示值的类型.
// 使你的函数在一个vector<int> 和 list<string>中查找给定值
#include <vector>
#include <list>
#include <string>
#include <iostream>
template <typename it_type, typename value_type>
it_type find(const it_type begin, const it_type end, const value_type val)
{
for (auto it = begin; it != end; it++)
{
if (*it == val)
return it;
}
return end;
}
template <typename it_type, typename value_type>
std::ostream &PrintFind(const it_type &begin, const it_type &end, const value_type &val, std::ostream &o = std::cout)
{
it_type find_it = find(begin, end, val);
if (find_it == end)
return o << "Null to find";
else
return o << *find_it;
}
int main(int argc, char const *argv[])
{
std::vector<int> vs({1, 2, 3, 4, 5, 6, 7, 8, 9});
std::cout << *find(vs.begin(), vs.end(), 2) << std::endl;
PrintFind(vs.begin(), vs.end(), 2) << std::endl;
PrintFind(vs.begin(), vs.end(), 10) << std::endl;
std::list<std::string> ls({"123", "456", "ABC"});
PrintFind(ls.begin(), ls.end(), "999") << std::endl;
while (1)
;
return 0;
}
16.5
// 16.5 编写一个printf 支持任意大小任意元素类型的数组
#include <iostream>
#include <string>
template <typename elem_type, int size>
void print(elem_type (&arr)[size])
{
for (auto ch : arr)
{
std::cout << ch << std::endl;
}
}
int main(int argc, char const *argv[])
{
std::string as[] = {"123", "456", "789"};
int ai[] = {1, 2, 3, 4, 5, 6};
print(as);
print(ai);
while (1)
;
return 0;
}
16.6
//16.6 你认为一个接受数组实参的标准库函数 begin 和end是如何工作的,定义你自己版本的begin和end
// 也就是定义 begin(array)和end(array)
#include <iostream>
#include <string>
template <typename elem_type, size_t size>
elem_type *Begin(elem_type (&array)[size])
{
return array;
}
template <typename elem_type, size_t size>
elem_type *End(elem_type (&array)[size])
{
return array + size;
}
int main(int argc, char const *argv[])
{
std::string as[] = {"123", "456", "789"};
for (auto i = Begin(as); i != End(as); i++)
std::cout << *i << std::endl;
while (1)
;
return 0;
}
16.7
//16.7 编写一个 constexpr 模版,返回给定参数的大小
template <typename T, unsigned N>
constexpr unsigned SizeOfArray(const T (&arr)[N])
{
return N;
}
16.8
//16.8 在97页非关键概念中,c++程序在for喜欢用!= 替换 < 的原因是什么
迭代器一般定义了 != 和= 但是不一定定义了<
16.9
// 16.9 什么是函数模版? 什么是类模版?
函数有参数是模版,编译器自动推断模版参数
类模版,有参数是模版,需要手动定义模版参数的
16.10
//16.10 当一个类模版被实例化,会发生什么?
1. 生成类的非模版函数
2. 生成需要使用的类的模版函数
16.11
// 16.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 *ptr, elemType value);
void insert(ListItem<elemType> *ptr, elemType value);
private:
//ListItem *front, *end;
ListItem<elemType> *front, *end;
};
16.12
// 16.12 编写你自己的 Blob 和 BlobPtr 模版,包含书中未定义的多个const成员
#include <vector>
#include <memory>
#include <string>
#include <iostream>
#include <initializer_list>
#include <exception>
#include <algorithm>
template <typename T>
class BlobPtr;
template <typename T>
class Blob;
template <typename T>
class ConstBlobPtr;
template <typename T>
bool operator==(const Blob<T> &a, const Blob<T> &b);
template <typename T>
bool operator!=(const Blob<T> &a, const Blob<T> &b);
template <typename T>
bool operator<(const Blob<T> &a, const Blob<T> &b);
template <typename T>
bool operator>(const Blob<T> &a, const Blob<T> &b);
template <typename T>
bool operator<=(const Blob<T> &a, const Blob<T> &b);
template <typename T>
bool operator>=(const Blob<T> &a, const Blob<T> &b);
template <typename T>
class Blob
{
friend BlobPtr<T>;
friend ConstBlobPtr<T>;
friend bool operator==<T>(const Blob<T> &a, const Blob<T> &b);
friend bool operator!=<T>(const Blob<T> &a, const Blob<T> &b);
// clang-format off
friend bool operator< <T>(const Blob<T> &a, const Blob<T> &b);
friend bool operator> <T>(const Blob<T> &a, const Blob<T> &b);
// clang-format on
friend bool operator<=<T>(const Blob<T> &a, const Blob<T> &b);
friend bool operator>=<T>(const Blob<T> &a, const Blob<T> &b);
// 以下两个应该是为了外部方便获取类型
typedef T value_type;
typedef typename std::vector<T>::size_type size_type;
private:
std::shared_ptr<std::vector<T>> data;
void check(size_type i, const std::string &msg) const;
public:
Blob() : data(std::make_shared<std::vector<T>>()) {}
Blob(const Blob<T> &s) : data(std::make_shared<std::vector<T>>(*s.data)) {}
Blob(Blob<T> &&a) noexcept : data(std::move(a.data)) {}
Blob &operator=(const Blob<T> &s);
Blob &operator=(Blob<T> &&) noexcept;
Blob(std::initializer_list<T> il) : data(std::make_shared<std::vector<T>>(il)) {}
size_type size() const { return data->size(); }
bool empty() const { return data->empty(); }
void push_back(T &val) { data->push_back(val); };
void push_back(const T &val) { data->push_back(std::move(val)); }
void pop_back();
T &back();
const T &back() const;
T &front();
const T &front() const;
T &operator[](size_type i);
const T &operator[](size_type i) const;
BlobPtr<T> begin();
BlobPtr<T> end();
ConstBlobPtr<T> cbegin() const;
ConstBlobPtr<T> cend() const;
void print(std::ostream &o);
};
template <typename T>
Blob<T> &Blob<T>::operator=(const Blob<T> &s)
{
data = std::make_shared<std::vector<T>>(*s.data);
return *this;
}
template <typename T>
Blob<T> &Blob<T>::operator=(Blob<T> &&s) noexcept
{
if (this != s)
{
data = std::move(s.data);
s.data = nullptr;
}
return *this;
}
template <typename T>
void Blob<T>::print(std::ostream &o)
{
for (auto ch : (*data))
o << ch << ",";
o << std::endl;
}
template <typename T>
void Blob<T>::check(size_type i, const std::string &msg) const
{
if (i >= size())
{
throw std::out_of_range(msg);
}
}
template <typename T>
void Blob<T>::pop_back()
{
check(0, "empty to pop_back");
data->pop_back();
}
template <typename T>
T &Blob<T>::back()
{
check(0, "Get empty Back()");
return data->back();
}
template <typename T>
const T &Blob<T>::back() const
{
check(0, "Get empty Back()");
return data->back();
}
template <typename T>
T &Blob<T>::front()
{
check(0, "Get empty front()");
return data->front();
}
template <typename T>
const T &Blob<T>::front() const
{
check(0, "Get empty front()");
return data->front();
}
template <typename T>
T &Blob<T>::operator[](size_type i)
{
check(i, "Get [" + std::to_string(i) + "]");
return data->at(i);
}
template <typename T>
const T &Blob<T>::operator[](size_type i) const
{
check(i, "Get [" + std::to_string(i) + "]");
return data->at(i);
}
template <typename T>
BlobPtr<T> Blob<T>::begin()
{
return BlobPtr<T>(*this);
}
template <typename T>
BlobPtr<T> Blob<T>::end()
{
return BlobPtr<T>(*this, size());
}
template <typename T>
ConstBlobPtr<T> Blob<T>::cbegin() const
{
return ConstBlobPtr<T>(*this);
}
template <typename T>
ConstBlobPtr<T> Blob<T>::cend() const
{
return ConstBlobPtr<T>(*this, size());
}
//****************************************************************************
template <typename T>
bool operator==(const Blob<T> &a, const Blob<T> &b)
{
return (*a.data == *b.data);
}
template <typename T>
bool operator!=(const Blob<T> &a, const Blob<T> &b)
{
return !(a == b);
}
template <typename T>
bool operator<(const Blob<T> &a, const Blob<T> &b)
{
return std::lexicographical_compare(a.data->begin(), a.data->end(), b.data->begin(), b.data->end());
}
template <typename T>
bool operator>(const Blob<T> &a, const Blob<T> &b)
{
return (b < a);
}
template <typename T>
bool operator<=(const Blob<T> &a, const Blob<T> &b)
{
return !(b > a);
}
template <typename T>
bool operator>=(const Blob<T> &a, const Blob<T> &b)
{
return !(a < b);
}
//****************************************************************************
// 定义 Blob::iterator
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>
bool operator<(const BlobPtr<T> &, const BlobPtr<T> &);
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>
bool operator>=(const BlobPtr<T> &, const BlobPtr<T> &);
template <typename T>
class BlobPtr
{
private:
std::weak_ptr<std::vector<T>> wptr;
std::size_t curr;
std::shared_ptr<std::vector<T>> check(size_t i, const std::string &msg) const;
public:
friend bool operator==<T>(const BlobPtr<T> &a, const BlobPtr<T> &b);
friend bool operator!=<T>(const BlobPtr<T> &a, const BlobPtr<T> &b);
// clang-format off
friend bool operator< <T>(const BlobPtr<T> &a, const BlobPtr<T> &b);
friend bool operator> <T>(const BlobPtr<T> &a, const BlobPtr<T> &b);
friend bool operator<=<T>(const BlobPtr<T> &a, const BlobPtr<T> &b);
friend bool operator>=<T>(const BlobPtr<T> &a, const BlobPtr<T> &b);
// clang-format on
BlobPtr() : curr(0) {}
BlobPtr(const Blob<T> &pt, size_t at = 0) : wptr(pt.data), curr(at) {}
T &operator*()
{
return check(curr, "Get elem[] out of range")->at(curr);
}
T &operator[](size_t at)
{
return check(at, "Get elem[] out of range")->at(at);
}
const T &operator*() const
{
return check(curr, "Get elem[] out of range")->at(curr);
}
const T &operator[](size_t at) const
{
return check(at, "Get elem[] out of range")->at(at);
}
const T *operator->() const
{
return &this->operator*();
}
T *operator->()
{
return &this->operator*();
}
BlobPtr operator++();
BlobPtr operator++(int);
BlobPtr operator--();
BlobPtr operator--(int);
BlobPtr &operator+=(size_t);
BlobPtr &operator-=(size_t);
BlobPtr operator+(size_t) const;
BlobPtr operator-(size_t) const;
};
template <typename T>
std::shared_ptr<std::vector<T>> BlobPtr<T>::check(size_t i, const std::string &msg) const
{
auto spt = wptr.lock();
if (spt)
{ // 使用之前必须复制到 shared_ptr
if (i >= spt->size())
throw std::out_of_range(msg);
}
else
{
throw std::runtime_error("unbound Blob<T>Ptr");
}
return spt;
}
// 前置++,先++,再返回
template <typename T>
BlobPtr<T> BlobPtr<T>::operator++()
{
check(curr, "++");
curr++;
return *this;
}
template <typename T>
BlobPtr<T> BlobPtr<T>::operator++(int)
{
auto ret = *this;
++*this;
return *this;
}
// 前置++,先++,再返回
template <typename T>
BlobPtr<T> BlobPtr<T>::operator--()
{
check(curr, "--");
curr--;
return *this;
}
template <typename T>
BlobPtr<T> BlobPtr<T>::operator--(int)
{
auto ret = *this;
--*this;
return *this;
}
template <typename T>
BlobPtr<T> &BlobPtr<T>::operator+=(size_t off)
{
curr += off;
check(curr, "increment past end of Blob<T>Ptr");
return *this;
}
template <typename T>
BlobPtr<T> &BlobPtr<T>::operator-=(size_t off)
{
curr -= off;
check(curr, "increment past end of Blob<T>Ptr");
return *this;
}
template <typename T>
BlobPtr<T> BlobPtr<T>::operator+(size_t off) const
{
BlobPtr<T> ret = *this;
ret += off;
return ret;
}
template <typename T>
BlobPtr<T> BlobPtr<T>::operator-(size_t off) const
{
BlobPtr<T> ret = *this;
ret -= off;
return ret;
}
//*********** friend *******************************//
template <typename T>
bool operator==(const BlobPtr<T> &a, const BlobPtr<T> &b)
{
return (a.curr == b.curr);
}
template <typename T>
bool operator!=(const BlobPtr<T> &a, const BlobPtr<T> &b)
{
return !(a == b);
}
template <typename T>
bool operator<(const BlobPtr<T> &a, const BlobPtr<T> &b)
{
return (a.curr < b.curr);
}
template <typename T>
bool operator>(const BlobPtr<T> &a, const BlobPtr<T> &b)
{
return (b < a);
}
template <typename T>
bool operator<=(const BlobPtr<T> &a, const BlobPtr<T> &b)
{
return !(a > b);
}
template <typename T>
bool operator>=(const BlobPtr<T> &a, const BlobPtr<T> &b)
{
return !(a < b);
}
// template <typename T>
// std::ostream &operator<<(std::ostream &o, const T &Blob_val)
// {
// }
//****************************************************************************
// 定义 Blob::iterator
template <typename T>
bool operator==(const ConstBlobPtr<T> &, const ConstBlobPtr<T> &);
template <typename T>
bool operator!=(const ConstBlobPtr<T> &, const ConstBlobPtr<T> &);
template <typename T>
bool operator<(const ConstBlobPtr<T> &, const ConstBlobPtr<T> &);
template <typename T>
bool operator>(const ConstBlobPtr<T> &, const ConstBlobPtr<T> &);
template <typename T>
bool operator<=(const ConstBlobPtr<T> &, const ConstBlobPtr<T> &);
template <typename T>
bool operator>=(const ConstBlobPtr<T> &, const ConstBlobPtr<T> &);
template <typename T>
class ConstBlobPtr
{
private:
std::weak_ptr<std::vector<T>> wptr;
std::size_t curr;
std::shared_ptr<std::vector<T>> check(size_t i, const std::string &msg) const;
public:
friend bool operator==<T>(const ConstBlobPtr<T> &a, const ConstBlobPtr<T> &b);
friend bool operator!=<T>(const ConstBlobPtr<T> &a, const ConstBlobPtr<T> &b);
// clang-format off
friend bool operator< <T>(const ConstBlobPtr<T> &a, const ConstBlobPtr<T> &b);
friend bool operator> <T>(const ConstBlobPtr<T> &a, const ConstBlobPtr<T> &b);
friend bool operator<=<T>(const ConstBlobPtr<T> &a, const ConstBlobPtr<T> &b);
friend bool operator>=<T>(const ConstBlobPtr<T> &a, const ConstBlobPtr<T> &b);
// clang-format on
ConstBlobPtr() : curr(0) {}
ConstBlobPtr(const Blob<T> &pt, size_t at = 0) : wptr(pt.data), curr(at) {}
const T &operator*() const
{
return check(curr, "Get elem[] out of range")->at(curr);
}
const T &operator[](size_t at) const
{
return check(at, "Get elem[] out of range")->at(at);
}
const T *operator->() const
{
return &this->operator*();
}
ConstBlobPtr operator++();
ConstBlobPtr operator++(int);
ConstBlobPtr operator--();
ConstBlobPtr operator--(int);
ConstBlobPtr &operator+=(size_t);
ConstBlobPtr &operator-=(size_t);
ConstBlobPtr operator+(size_t) const;
ConstBlobPtr operator-(size_t) const;
};
template <typename T>
std::shared_ptr<std::vector<T>> ConstBlobPtr<T>::check(size_t i, const std::string &msg) const
{
auto spt = wptr.lock();
if (spt)
{ // 使用之前必须复制到 shared_ptr
if (i >= spt->size())
throw std::out_of_range(msg);
}
else
{
throw std::runtime_error("unbound Blob<T>Ptr");
}
return spt;
}
// 前置++,先++,再返回
template <typename T>
ConstBlobPtr<T> ConstBlobPtr<T>::operator++()
{
check(curr, "++");
curr++;
return *this;
}
template <typename T>
ConstBlobPtr<T> ConstBlobPtr<T>::operator++(int)
{
auto ret = *this;
++*this;
return *this;
}
// 前置++,先++,再返回
template <typename T>
ConstBlobPtr<T> ConstBlobPtr<T>::operator--()
{
check(curr, "--");
curr--;
return *this;
}
template <typename T>
ConstBlobPtr<T> ConstBlobPtr<T>::operator--(int)
{
auto ret = *this;
--*this;
return *this;
}
template <typename T>
ConstBlobPtr<T> &ConstBlobPtr<T>::operator+=(size_t off)
{
curr += off;
check(curr, "increment past end of Blob<T>Ptr");
return *this;
}
template <typename T>
ConstBlobPtr<T> &ConstBlobPtr<T>::operator-=(size_t off)
{
curr -= off;
check(curr, "increment past end of Blob<T>Ptr");
return *this;
}
template <typename T>
ConstBlobPtr<T> ConstBlobPtr<T>::operator+(size_t off) const
{
ConstBlobPtr<T> ret = *this;
ret += off;
return ret;
}
template <typename T>
ConstBlobPtr<T> ConstBlobPtr<T>::operator-(size_t off) const
{
ConstBlobPtr<T> ret = *this;
ret -= off;
return ret;
}
//*********** friend *******************************//
template <typename T>
bool operator==(const ConstBlobPtr<T> &a, const ConstBlobPtr<T> &b)
{
return (a.curr == b.curr);
}
template <typename T>
bool operator!=(const ConstBlobPtr<T> &a, const ConstBlobPtr<T> &b)
{
return !(a == b);
}
template <typename T>
bool operator<(const ConstBlobPtr<T> &a, const ConstBlobPtr<T> &b)
{
return (a.curr < b.curr);
}
template <typename T>
bool operator>(const ConstBlobPtr<T> &a, const ConstBlobPtr<T> &b)
{
return (b < a);
}
template <typename T>
bool operator<=(const ConstBlobPtr<T> &a, const ConstBlobPtr<T> &b)
{
return !(a > b);
}
template <typename T>
bool operator>=(const ConstBlobPtr<T> &a, const ConstBlobPtr<T> &b)
{
return !(a < b);
}
int main(int argc, char const *argv[])
{
try
{
std::string hello = "hello";
Blob<std::string> blob_s({"1"});
blob_s.pop_back();
// 触发check 异常
//blob_s.pop_back();
blob_s.push_back("2");
blob_s.push_back(hello);
//
std::cout << blob_s.size() << std::endl;
blob_s.print(std::cout);
std::cout << blob_s.back() << std::endl;
std::cout << blob_s[blob_s.size() - 1] << std::endl;
// 测试 iterator
BlobPtr<std::string> blob_pt1(blob_s);
std::cout << blob_pt1[1] << std::endl;
// 测试迭代器
std::cout << "By iterator" << std::endl;
for (auto b = blob_s.begin(); b != blob_s.end(); b++)
{
std::cout << *b << std::endl;
}
}
catch (const std::exception &msg)
{
std::cout << msg.what() << std::endl;
}
{
Blob<std::string> sb1{"a", "b", "c"};
Blob<std::string> sb2 = sb1;
sb2[2] = "b";
if (sb1 > sb2)
{
for (auto iter = sb2.cbegin(); iter != sb2.cend(); ++iter)
std::cout << *iter << " ";
std::cout << std::endl;
}
ConstBlobPtr<std::string> iter(sb2);
std::cout << iter->size() << std::endl;
}
while (1)
;
return 0;
}
16.13
// 16.3 解释你为BlobPtr的相等和关系运算符选择那种类型的友好关系
模版参数T 一直的 重载都为其友元关系
16.14
16.15
// 16.14 编写Screen类模版,用非类型参数定义Screen的高和宽,可以当宏使用
#include <string>
#include <algorithm>
#include <iostream>
//using pos = int;
using pos = std::string::size_type;
template <pos, pos>
class Screen;
template <pos H, pos W>
std::istream &operator>>(std::istream &, Screen<H, W> &);
template <pos H, pos W>
std::ostream &operator<<(std::ostream &, const Screen<H, W> &);
template <pos H, pos W>
class Screen
{
friend std::istream &operator>><H, W>(std::istream &, Screen<H, W> &);
friend std::ostream &operator<<<H, W>(std::ostream &, const Screen<H, W> &);
private:
pos curr = 0;
std::string contents;
public:
Screen() = default;
Screen(char c) : contents(H * W, c) {}
char get() const { return contents[curr]; }
char get(pos a, pos b) const { return contents[a * W + b]; }
Screen &set(char c)
{
contents[curr++] = c;
curr = std::min(curr, H * W);
return *this;
}
Screen &set(pos a, pos b, char c)
{
contents[a * W + b] = c;
return *this;
}
Screen &move(pos a, pos b);
};
template <pos H, pos W>
Screen<H, W> &Screen<H, W>::move(pos a, pos b)
{
curr = a * W + b;
return *this;
}
template <pos H, pos W>
std::istream &operator>>(std::istream &is, Screen<H, W> &s)
{
std::string input;
is >> input;
for (char ch : input)
s.set(ch);
return is;
}
template <pos H, pos W>
std::ostream &operator<<(std::ostream &os, const Screen<H, W> &s)
{
for (pos r = 0; r != H; ++r)
{
for (pos c = 0; c != W; ++c)
{
os << s.get(r, c);
}
os << std::endl;
}
return os;
}
int main()
{
Screen<5, 5> screen(‘x‘);
screen.set(2, 2, ‘o‘);
std::cout << screen << std::endl;
std::cout << "please input some characters as you like:";
std::cin >> screen;
std::cout << screen << std::endl;
while (1)
;
{
/* code */
}
}
// xxxxx
// xxxxx
// xxoxx
// xxxxx
// xxxxx
// please input some characters as you like:1234512345123451234512345999
// 12345
// 12345
// 12345
// 12345
// 12345
16.17
在定义模板参数类型的时候,typename 和class 没什么不同
在 有些时候需要使用 模板类的类型的时候,需要加上typename,因为默认识别为类的成员 p593
T::x a; T是模板,有两种理解
1. x是静态成员,与a相*------默认是这个
2. 定义一个a变量,类型是T::x
16.18
// 解释下面的代码是否合法
> (a) template <typename T, U, typename V> void f1(T, U, V);
> (b) template <typename T> T f2(int &T);
> (c) inline template <typename T> T foo(T, unsigned int*);
> (d) template <typename T> f4(T, T);
> (e) typedef char Ctype;
> template <typename Ctype> Ctype f5(Ctype a);
a. template <typename T, typename U, typename V> void f1(T, U, V);
b. template <typename T> T f2(int &a); // typename 被隐藏
c. template <typename T> inline T foo(T, unsigned int*); //inline的位置
d. template <typename T> void f4(T, T); // 提供返回类型
e. 隐藏了typedef
16.19
16.20
// 16.19 接受容器的引用,打印容器的元素.使用 size_type 和size 控制打印
// 16.20 使用迭代器
#include <iostream>
#include <vector>
#include <list>
#include <string>
using namespace std;
template <typename T>
printv(const T &v)
{
for (typename T::size_type i = 0; i < v.size(); i++)
cout << v.at(i) << "<>";
cout << endl;
}
template <typename T>
printv2(const T &v)
{
for (auto c = v.begin(); c != v.end(); c++)
cout << *c << "<>";
cout << endl;
}
int main(int argc, char const *argv[])
{
vector<int> v1({1, 2, 3, 4, 5, 6, 7});
printv(v1);
//因为printv使用的是 size 不是迭代器,所以不能用list
list<string> v2({"123", "456"});
printv2(v2);
while (1)
;
return 0;
}
16.21
// 16.21 实现自己的DebugDelete版本
#include <iostream>
#include <memory>
using namespace std;
class DebugDelete
{
private:
std::ostream &dbgos;
public:
DebugDelete(std::ostream &o = std::cout) : dbgos(o) {}
template <typename T>
void operator()(T *elem) const
{
dbgos << "delete by DebugDelete" << std::endl;
delete elem;
}
};
int main(int argc, const char **argv)
{
DebugDelete d;
int *a = new int;
d(a);
int *b = new int;
DebugDelete()(b);
{
std::unique_ptr<int, DebugDelete> ui(new int, DebugDelete());
// ui.release(); 释放ui但保留内存
// ui.reset(); 释放ui内存的东西,会调用DebugDelete
std::cout << "/* message */" << std::endl;
}
while (1)
;
return 0;
}
16.24
// 16.24 为你的Blob添加一个构造函数支持两个迭代器
// 16.12 编写你自己的 Blob 和 BlobPtr 模版,包含书中未定义的多个const成员
// 16.24
template <typename type_it>
Blob(type_it a, type_it b) : data(std::make_shared<std::vector<T>>(a, b)) {}
try
{
std::list<std::string> ls({"A1", "A2", "A3"});
Blob<std::string> blob_s(ls.begin(), ls.end());
blob_s.print(std::cout);
}
catch (const std::exception &e)
{
std::cerr << e.what() << ‘
‘;
}
16.25
// 16.25 解释下面的语句
// 显式实例化,需要在其他的地方定义
extern template class vector<string>;
// 定义
template class vector<Sales_data>;
16.26
// 16.26 假设Nodefault 是一个没有默认构造函数的类,我们可以显式实例化 vecctor<NoDefault> 吗?
不能,因为vector需要调用默认的构造函数
16.27
// 16.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 有,这是一个非模板的类,创造对象
d 没有,指针和引用不需要
e 有 Stack<char>
f 编译阶段实例化,但是只是临时的应该
*/
// 测试方法
// Solution from [How is a template instantiated? - Stack Overflow](https://stackoverflow.com/questions/21598635/how-is-a-template-instantiated)
#include <iostream>
using namespace std;
template <typename T> class Stack {
typedef typename T::ThisDoesntExist StaticAssert; // T::NotExisting doesn‘t exist at all!
};
void f1(Stack<char>); // No instantiation, compiles
class Exercise {
Stack<double> &rsd; // No instantiation, compiles (references don‘t need instantiation, are similar to pointers in this)
Stack<int> si; // Instantiation! Doesn‘t compile!!
};
int main(){
Stack<char> *sc; // No Instantiation, this compiles successfully since a pointer doesn‘t need instantiation
f1(*sc); // Instantiation of Stack<char>! Doesn‘t compile!!
int iObj = sizeof(Stack< std::string >); // Instantiation of Stack<std::string>, doesn‘t compile!!
}
16.28
// 16.28 编写自己的shared_ptr 和 unique_ptr
// shared_ptr 保存一个指针,参数为value type* 按照delete是返回void的函数 using DelFuncPtr = void (*)(T*);
// unique_ptr 保存的是删除器的类,因为不会被改变
#include <vector>
#include <string>
#include <iostream>
//*********************************************************************************************************
/*
0. 参考声明 template< class T > class shared_ptr;
1. 内容指针,引用计数指针,删除器的函数指针
2. 构造函数参考,先实现默认构造,即带一个指针和一个删除器
3. 拷贝构造
4. 拷贝赋值,这里需要释放原来的空间,可以使用 swap 传递值的方式 来实现
5. 所以我们先实现swap void swap( shared_ptr& r ) noexcept;
6. 析构函数,判断引用,判断是否nullptr
7. void reset( Y* ptr, Deleter d );
*/
template <class T>
class SharedPtr
{
using DelFuncPtr = void (*)(T *);
private:
T *ptr_ = nullptr;
size_t *count_ptr_ = nullptr;
DelFuncPtr del_ = nullptr;
public:
SharedPtr(T *ptr = nullptr, DelFuncPtr del = nullptr) : ptr_(ptr), del_(del), count_ptr_(new size_t(ptr_ != nullptr)) {}
SharedPtr(const SharedPtr &s) : ptr_(s.ptr_), del_(s.del_), count_ptr_(s.count_ptr_)
{
++*s.count_ptr_;
}
SharedPtr &operator=(SharedPtr s) //这里使用值传递,会先复制一份
{
//交换自己和临时的,临时的在退出的时候会自动调用析构释放
swap(s);
return *this;
}
void reset(T *ptr = nullptr, DelFuncPtr d = nullptr)
{
auto news = SharedPtr(ptr, d);
swap(news);
}
void swap(SharedPtr &r) noexcept
{
using std::swap;
swap(ptr_, r.ptr_);
swap(count_ptr_, r.count_ptr_);
swap(del_, r.del_);
}
~SharedPtr()
{
if (ptr_ == nullptr)
return;
if (0 == --*count_ptr_)
{
del_ == nullptr ? delete (ptr_) : del_(ptr_);
delete count_ptr_;
}
ptr_ = nullptr;
count_ptr_ = nullptr;
}
T *get() const noexcept
{
return ptr_;
}
size_t use_count() const noexcept
{
return *count_ptr_;
}
T &operator*() const noexcept
{
return *ptr_;
}
T *operator->() const noexcept
{
return ptr_;
}
//检查 *this 是否存储非空指针,即是否有 get() != nullptr
explicit operator bool() const noexcept
{
return get() != nullptr;
}
};
//*********************************************************************************************************
/**unique_ptr 保存的是删除器的类,因为不会被改变
*/
class Delete
{
public:
template <typename T>
void operator()(T *ptr) const { delete ptr; }
};
template <typename T, typename D = Delete>
class UniquePtr
{
private:
T *ptr_ = nullptr;
D del_;
public:
UniquePtr(T *ptr = nullptr, const D &d = D()) : ptr_(ptr), del_(d) {}
~UniquePtr() { del_(ptr_); }
UniquePtr(const UniquePtr &) = delete;
UniquePtr &operator=(const UniquePtr &) = delete;
UniquePtr(UniquePtr &&other) noexcept : ptr_(other.ptr_), del_(std::move(other.del_))
{
other.ptr_ = nullptr;
}
UniquePtr &operator=(UniquePtr &&other) noexcept
{
if (this != &other)
{
reset();
ptr_ = other.ptr_;
del_ = std::move(other.del_);
other.ptr_ = nullptr;
}
return *this;
}
UniquePtr &operator=(std::nullptr_t) noexcept
{
reset();
return *this;
}
T *release() noexcept
{
T *ret = ptr_;
ptr_ = nullptr;
return ret;
}
void reset(T *ptr = nullptr)
{
del_(ptr_);
ptr_ = ptr;
}
void swap(UniquePtr &other) noexcept
{
using std::swap;
swap(ptr_, other.ptr_);
swap(del_, other.del_);
}
T *get() const noexcept
{
return ptr_;
}
D &get_deleter() noexcept { return del_; }
const D &get_deleter() const noexcept { return del_; }
T &operator*() const noexcept
{
return *ptr_;
}
T *operator->() const noexcept
{
return ptr_;
}
T &operator[](size_t i) const { return ptr_[i]; }
//检查 *this 是否存储非空指针,即是否有 get() != nullptr
explicit operator bool() const noexcept
{
return get() != nullptr;
}
};
//*********************************************************************************************************
class DebugDelete
{
private:
std::ostream &dbgos;
public:
DebugDelete(std::ostream &o = std::cout) : dbgos(o) {}
template <typename T>
void operator()(T *elem) const
{
dbgos << "delete by DebugDelete" << std::endl;
delete elem;
}
};
int main(int argc, const char **argv)
{
{
using std::string;
using std::vector;
DebugDelete d;
string *mystring = new string("123456");
//SharedPtr<string> myshared_string(mystring, [](string *p) {std::cout << "Call delete from lambda...
";delete p; });
SharedPtr<string> myshared_string(mystring, [](string *p) { DebugDelete()(p); });
std::cout << *myshared_string.get() << std::endl;
std::cout << "Test Copy assiment" << std::endl;
SharedPtr<string> myshared_string3(new string("55555"));
myshared_string3 = myshared_string;
std::cout << "Test Copy Construct" << std::endl;
SharedPtr<string> myshared_string2(myshared_string);
std::cout << myshared_string.use_count() << std::endl;
}
{
using std::string;
using std::vector;
std::cout << "Test Unique Ptr" << std::endl;
UniquePtr<string> s1(new string("A10086"));
std::cout << *s1.get() << std::endl;
UniquePtr<string, DebugDelete> s2(new string("B10086"), DebugDelete());
std::cout << *s1.get() << std::endl;
}
while (1)
;
return 0;
}
16.29
16.30
// 修改你的Blob类,用你的shared_ptr 替代标准库的版本
// 这里不能使用weak_ptr了,应该自己再做一个,
// 在赋值迭代器的时候,weak_ptr 的赋值操作没有对 我们自己实现的 重载
// 这里我们修改Blob 不使用迭代器了
// 16.24 为你的Blob添加一个构造函数支持两个迭代器
// 16.12 编写你自己的 Blob 和 BlobPtr 模版,包含书中未定义的多个const成员
#include <vector>
#include <memory>
#include <string>
#include <list>
#include <iostream>
#include <initializer_list>
#include <exception>
#include <algorithm>
template <class T>
class SharedPtr
{
using DelFuncPtr = void (*)(T *);
private:
T *ptr_ = nullptr;
size_t *count_ptr_ = nullptr;
DelFuncPtr del_ = nullptr;
public:
SharedPtr(T *ptr = nullptr, DelFuncPtr del = nullptr) : ptr_(ptr), del_(del), count_ptr_(new size_t(ptr_ != nullptr)) {}
SharedPtr(const SharedPtr &s) : ptr_(s.ptr_), del_(s.del_), count_ptr_(s.count_ptr_)
{
++*s.count_ptr_;
}
SharedPtr &operator=(SharedPtr s) //这里使用值传递,会先复制一份
{
//交换自己和临时的,临时的在退出的时候会自动调用析构释放
swap(s);
return *this;
}
void reset(T *ptr = nullptr, DelFuncPtr d = nullptr)
{
auto news = SharedPtr(ptr, d);
swap(news);
}
void swap(SharedPtr &r) noexcept
{
using std::swap;
swap(ptr_, r.ptr_);
swap(count_ptr_, r.count_ptr_);
swap(del_, r.del_);
}
~SharedPtr()
{
if (ptr_ == nullptr)
return;
if (0 == --*count_ptr_)
{
del_ == nullptr ? delete (ptr_) : del_(ptr_);
delete count_ptr_;
}
ptr_ = nullptr;
count_ptr_ = nullptr;
}
T *get() const noexcept
{
return ptr_;
}
size_t use_count() const noexcept
{
return *count_ptr_;
}
T &operator*() const noexcept
{
return *ptr_;
}
T *operator->() const noexcept
{
return ptr_;
}
//检查 *this 是否存储非空指针,即是否有 get() != nullptr
explicit operator bool() const noexcept
{
return get() != nullptr;
}
};
template <typename T>
class Blob;
template <typename T>
bool operator==(const Blob<T> &a, const Blob<T> &b);
template <typename T>
bool operator!=(const Blob<T> &a, const Blob<T> &b);
template <typename T>
bool operator<(const Blob<T> &a, const Blob<T> &b);
template <typename T>
bool operator>(const Blob<T> &a, const Blob<T> &b);
template <typename T>
bool operator<=(const Blob<T> &a, const Blob<T> &b);
template <typename T>
bool operator>=(const Blob<T> &a, const Blob<T> &b);
template <typename T>
class Blob
{
friend bool operator==<T>(const Blob<T> &a, const Blob<T> &b);
friend bool operator!=<T>(const Blob<T> &a, const Blob<T> &b);
// clang-format off
friend bool operator< <T>(const Blob<T> &a, const Blob<T> &b);
friend bool operator> <T>(const Blob<T> &a, const Blob<T> &b);
// clang-format on
friend bool operator<=<T>(const Blob<T> &a, const Blob<T> &b);
friend bool operator>=<T>(const Blob<T> &a, const Blob<T> &b);
// 以下两个应该是为了外部方便获取类型
typedef T value_type;
typedef typename std::vector<T>::size_type size_type;
private:
SharedPtr<std::vector<T>> data;
void check(size_type i, const std::string &msg) const;
public:
Blob() : data(new std::vector<T>()) {}
// 16.24
template <typename type_it>
Blob(type_it a, type_it b) : data(new std::vector<T>(a, b)) {}
Blob(const Blob<T> &s) : data(new std::vector<T>(*s.data)) {}
Blob(Blob<T> &&a) noexcept : data(std::move(a.data)) {}
Blob &operator=(const Blob<T> &s);
Blob &operator=(Blob<T> &&) noexcept;
Blob(std::initializer_list<T> il) : data(new std::vector<T>(il)) {}
size_type size() const { return data->size(); }
bool empty() const { return data->empty(); }
void push_back(T &val) { data->push_back(val); };
void push_back(const T &val) { data->push_back(std::move(val)); }
void pop_back();
T &back();
const T &back() const;
T &front();
const T &front() const;
T &operator[](size_type i);
const T &operator[](size_type i) const;
void print(std::ostream &o);
};
template <typename T>
Blob<T> &Blob<T>::operator=(const Blob<T> &s)
{
data = new std::vector<T>(*s.data);
return *this;
}
template <typename T>
Blob<T> &Blob<T>::operator=(Blob<T> &&s) noexcept
{
if (this != s)
{
data = std::move(s.data);
s.data = nullptr;
}
return *this;
}
template <typename T>
void Blob<T>::print(std::ostream &o)
{
for (auto ch : (*data))
o << ch << ",";
o << std::endl;
}
template <typename T>
void Blob<T>::check(size_type i, const std::string &msg) const
{
if (i >= size())
{
throw std::out_of_range(msg);
}
}
template <typename T>
void Blob<T>::pop_back()
{
check(0, "empty to pop_back");
data->pop_back();
}
template <typename T>
T &Blob<T>::back()
{
check(0, "Get empty Back()");
return data->back();
}
template <typename T>
const T &Blob<T>::back() const
{
check(0, "Get empty Back()");
return data->back();
}
template <typename T>
T &Blob<T>::front()
{
check(0, "Get empty front()");
return data->front();
}
template <typename T>
const T &Blob<T>::front() const
{
check(0, "Get empty front()");
return data->front();
}
template <typename T>
T &Blob<T>::operator[](size_type i)
{
check(i, "Get [" + std::to_string(i) + "]");
return data->at(i);
}
template <typename T>
const T &Blob<T>::operator[](size_type i) const
{
check(i, "Get [" + std::to_string(i) + "]");
return data->at(i);
}
//****************************************************************************
template <typename T>
bool operator==(const Blob<T> &a, const Blob<T> &b)
{
return (*a.data == *b.data);
}
template <typename T>
bool operator!=(const Blob<T> &a, const Blob<T> &b)
{
return !(a == b);
}
template <typename T>
bool operator<(const Blob<T> &a, const Blob<T> &b)
{
return std::lexicographical_compare(a.data->begin(), a.data->end(), b.data->begin(), b.data->end());
}
template <typename T>
bool operator>(const Blob<T> &a, const Blob<T> &b)
{
return (b < a);
}
template <typename T>
bool operator<=(const Blob<T> &a, const Blob<T> &b)
{
return !(b > a);
}
template <typename T>
bool operator>=(const Blob<T> &a, const Blob<T> &b)
{
return !(a < b);
}
int main(int argc, const char **argv)
{
Blob<std::string> blob_s({"1", "2"});
blob_s.print(std::cout);
while (1)
;
return 0;
}
16.31
// 16.31 如果我们将DebugDelete 与 unique_ptr 一起使用,解释编译器将删除器处理为内联形式的可能?
// The compiler will set the default deleter type as `DebugDelete`,
// which will be executed is known at compile time.
编译的时候就传递赋值
16.32
// 在模版实参推断过程中发生了什么?
1. 类型推导
2. 实例化函数
16.33
指出在模版实参推断过程中允许对函数实参进行的两种类型转换。
1. const转换, 实参是非const的传递给形参是const的
2. 数组或函数转换为指针,注意:形参不能是引用,形参是引用,传递的是数组,含有纬度的
16.34
对下面的代码解释每个调用是否合法。如果合法,T 的类型是什么?如果不合法,为什么?
template <class T> int compare(const T&, const T&);
(a) compare("hi", "world");
(b) compare("bye", "dad");
a. 不合法,这里传递的是char(&a)[3] char(&a)[6]
b. 合法
13.35
13.35 下面调用中哪些是错误的(如果有的话)?如果调用合法,T 的类型是什么?如果调用不合法,问题何在?
template <typename T> T calc(T, int);
tempalte <typename T> T fcn(T, T);
double d; float f; char c;
(a) calc(c, ‘c‘);
(b) calc(d, f);
(c) fcn(c, ‘c‘);
(d) fcn(d, f);
a. 合法, T=char
b. 合法,T=double
c 合法 char,
d 不合法,不能发生类型转换
16.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);
(b) f2(p1, p2);
(c) f1(cp1, cp2);
(d) f2(cp1, cp2);
(e) f1(p1, cp1);
(f) f2(p1, cp1);
// f1没有返回值 哈哈哈
a. f1(int*,int*);
b. f2(int*,int*);
c. f1(const int*,const int*);
d. f2(const int*,const int*);
e. error
f. f2( int*,const int*);
16.37
标准库 max 函数有两个参数,它返回实参中的较大者。此函数有一个模版类型参数。
你能在调用 max 时传递给它一个 int 和一个 double 吗?如果可以,如何做?如果不可以,为什么?
指定参数类型 max<double>(a, b);
#include <iostream>
#include <string>
#include <algorithm>
using namespace std;
int main(int argc, char const *argv[])
{
int a = 6;
double b = 6.1231;
std::cout << std::max<long double>(a, b) << std::endl;
// initializer_list<T> 也是模版,不会发生转换
//std::cout << std::max({a, b}, [](const int a, const int b) { return a < b; }) << std::endl;
std::cout << std::max({1, 2}, [](const int a, const int b) { return a < b; }) << std::endl;
while (1)
;
return 0;
}
16.38
当我们调用 make_shared 时,必须提供一个显示模版实参。解释为什么需要显式模版实参以及它是如果使用的。
返回类型无法推断
16.39
对16.1.1节 578 中的原始版本的 compare 函数,使用一个显式模版实参,使得可以向函数传递两个字符串字面量。
compare<std::string>("a", "bb");
16.40
下面的函数是否合法?如果不合法,为什么?如果合法,对可以传递的实参类型有什么限制(如果有的话)?
返回类型是什么?
合法,元素支持+ 返回类型由operator+决定
template <typename It>
auto fcn3(It beg, It end) -> decltype(*beg + 0)
{
//处理序列
return *beg;
}
16.41
编写一个新的 sum 版本,它返回类型保证足够大,足以容纳加法结果
template <typename T1, typename T2>
auto sum(T1 a, T2 b) -> decltype(a + b) {
return a + b;
}
16.42
// 对下面每个调用,确定 T 和 val 的类型:
template <typename T> void g(T&& val);
int i = 0; const int ci = i;
(a) g(i);
(b) g(ci);
(c) g(i * ci);
a. i是左值,所以会传递左值引用 int& && 折叠为 int&
b. ci 是const int, 折叠为const int&
c. i*ci是一个局部的变量,是个右值int&& 折叠为int&&
16.43
使用上一题定义的函数,如果我们调用g(i = ci),g 的模版参数将是什么?
i=ci 返回的是 i 也就是 int,折叠为int&
#include <iostream>
int main(int argc, char const *argv[])
{
int i = 0;
++(i = 3);
std::cout << i << std::endl; //4
while (1)
;
return 0;
}
16.44
使用与第一题中相同的三个调用,如果 g 的函数参数声明为 T(而不是T&&),确定T的类型。
如果g的函数参数是 const T&呢?
template <typename T> void g(T val);
int i = 0; const int ci = i;
(a) g(i); int
(b) g(ci); int
(c) g(i * ci); int
template <typename T> void g(const& val);
int i = 0; const int ci = i;
(a) g(i); int
(b) g(ci); int
(c) g(i * ci); int
16.45
如果下面的模版,如果我们对一个像42这样的字面常量调用g,解释会发生什么?
如果我们对一个int 类型的变量调用g 呢?
template <typename T> void g(T&& val) { vector<T> v; }
g(42) 传递的就是 int&&
{
vector<int&&> v;
}
int i;
g(i) // 传递是 int& && 折叠为 int&
{
vector<int&> v; //报错,无法保存引用
}
16.46
// 解释下面的循环,它来自13.5节中的 StrVec::reallocate:
// alloc.construct(dest++, std::move(*elem++));
// *elem 为左值,转换为 elem的右值
template< class U, class... Args >
void construct( U* p, Args&&... args );
16.47
#include <iostream>
void f(int v1, int &v2)
{
std::cout << v1 << " " << ++v2 << std::endl;
}
void g(int &&i, int &j)
{
std::cout << i << " " << ++j << std::endl;
}
template <typename F, typename T1, typename T2>
void flip(F f, T1 &&t1, T2 &&t2)
{
f(std::forward<T2>(t2), std::forward<T1>(t1));
}
int main()
{
int j = 0;
flip(f, j, 42);
flip(g, j, 42);
while (1)
;
}
16.48
// 16.48 编写你自己版本的 debug_rep 函数。
#include <iostream>
#include <string>
#include <sstream>
using std::string;
template <typename T>
string debug_rep(const T &t)
{
std::ostringstream ret;
ret << t;
return ret.str();
}
template <typename T>
string debug_rep(T *t)
{
std::ostringstream ret;
ret << "Point Addr=" << t;
if (t == nullptr)
ret << "nullptr";
else
ret << debug_rep(*t);
return ret.str();
}
string debug_rep(const string &t)
{
return "String:" + t;
}
string debug_rep(char *t)
{
return "char*" + debug_rep(string(t));
}
string debug_rep(const char *t)
{
return "const char*" + debug_rep(string(t));
}
int main(int argc, const char **argv)
{
std::cout << debug_rep("123456") << std::endl;
while (1)
;
return 0;
}
16.49
//16.49解释下面每个调用会发生什么
template <typename T> void f(T);
template <typename T> void f(const T*);
template <typename T> void g(T);
template <typename T> void g(T*);
int i = 42, *p = &i;
const int ci = 0, *p2 = &ci;
g(42); g(p); g(ci); g(p2);
f(42); f(p); f(ci); f(p2);
g(42) T=int 3.template <typename T> void g(T);
g(p) T=int 4.template <typename T> void g(T*);
g(ci) T=const int 3.template <typename T> void g(T);
g(p2) T=const int 4.template <typename T> void g(T*);
f(42) T=int 1.template <typename T> void f(T);
f(p) T=int* 1.template <typename T> void f(T);
f(ci) T=const int 1.template <typename T> void f(T);
f(p2) T=int 2.template <typename T> void f(const T*);
16.50
// 16.50 定义上一个练习中的函数,令它们打印一条身份信息。运行该练习中的代码。如果函数调用的行为与你预期不符,确定你理解了原因。
/*
template <typename T> void f(T);
template <typename T> void f(const T*);
template <typename T> void g(T);
template <typename T> void g(T*);
int i = 42, *p = &i;
const int ci = 0, *p2 = &ci;
g(42); g(p); g(ci); g(p2);
f(42); f(p); f(ci); f(p2);
g(42) T=int 3.template <typename T> void g(T);
g(p) T=int 4.template <typename T> void g(T*);
g(ci) T=const int 3.template <typename T> void g(T);
g(p2) T=const int 4.template <typename T> void g(T*);
f(42) T=int 1.template <typename T> void f(T);
f(p) T=int* 1.template <typename T> void f(T);
f(ci) T=const int 1.template <typename T> void f(T);
f(p2) T=int 2.template <typename T> void f(const T*);
*/
#include <iostream>
#include <string>
template <typename T>
void f(T)
{
std::cout << "1" << std::endl;
}
template <typename T>
void f(const T *)
{
std::cout << "2" << std::endl;
}
template <typename T>
void g(T)
{
std::cout << "3" << std::endl;
}
template <typename T>
void g(T *)
{
std::cout << "4" << std::endl;
}
int main(int argc, char const *argv[])
{
int i = 42, *p = &i;
const int ci = 0, *p2 = &ci;
g(42);
g(p);
g(ci);
g(p2);
f(42);
f(p);
f(ci);
f(p2);
while (1)
;
return 0;
}
16.51
16.52
// 16.51 调用本节中的每个 foo,确定 sizeof…(Args) 和 sizeof…(rest)分别返回什么。
#include <iostream>
using namespace std;
template <typename T, typename... Args>
void foo(const T &t, const Args &... rest)
{
std::cout << "sizeof...(Args)=" << sizeof...(Args) << " sizeof...(rest)=" << sizeof...(rest) << std::endl;
}
int main(int argc, const char **argv)
{
int i = 0;
double d = 3.14;
string s = "how";
foo(i, s, 42, d); /// sizeof...(Args)=3 sizeof...(rest)=3
foo(s, 42, "hi"); /// sizeof...(Args)=2 sizeof...(rest)=2
foo(d, s); /// sizeof...(Args)=1 sizeof...(rest)=1
foo("hi"); /// sizeof...(Args)=0 sizeof...(rest)=0
foo(i, s, s, d); /// sizeof...(Args)=3 sizeof...(rest)=3
while (1)
;
return 0;
}
16.53
// 16.53 编写你自己版本的 print 函数,并打印一个、两个及五个实参来测试它,要打印的每个实参都应有不同的类型。
#include <iostream>
using namespace std;
template <typename T>
ostream &print(ostream &o, const T &s)
{
return o << s << endl;
}
template <typename A, typename... T>
ostream &print(ostream &o, const A &a, const T &... s)
{
o << a << ",";
return print(o, s...);
}
int main(int argc, const char **argv)
{
int i = 1;
double d = 3.14;
float f = 99.99;
string s = "hello";
print(std::cout, i);
print(std::cout, i, s);
print(std::cout, i, d, f, s, "123456");
while (1)
;
return 0;
}
16.54
如果我们对一个没 << 运算符的类型调用 print,会发生什么?
编译实例化失败.
//no match for ‘operator<<‘ (operand types are ‘std::ostream {aka std::basic_ostream<char>}‘ and ‘const std::vector<int>‘)
//print(std::cout, i, d, f, s, vector<int>(1));
16.55
// 如果我们的可变参数版本 print 的定义之后声明非可变参数版本,解释可变参数的版本会如何执行。
我们最终会执行到只有 ostream,没有第二个参数 print(std::ostream&)
但是我们的模版函数是 ostream &print(ostream &o, const A &a, const T &... s)
也就是没有匹配一个参数的版本
16.56
// 16.56 编写并测试可变参数版本的 errorMsg。
#include <initializer_list>
#include <iostream>
#include <string>
#include <sstream>
using namespace std;
/*********************************************************************/
template <typename T>
string debug_rep(const T &t)
{
std::ostringstream ret;
ret << t;
return ret.str();
}
template <typename T>
string debug_rep(T *t)
{
std::ostringstream ret;
ret << "Point Addr=" << t;
if (t == nullptr)
ret << "nullptr";
else
ret << debug_rep(*t);
return ret.str();
}
string debug_rep(const string &t)
{
return "String:" + t;
}
string debug_rep(char *t)
{
return "char*" + debug_rep(string(t));
}
string debug_rep(const char *t)
{
return "const char*" + debug_rep(string(t));
}
/*********************************************************************/
void err_msg_old(initializer_list<string> il)
{
for (auto ch : il)
std::cout << ch << ",";
std::cout << std::endl;
}
template <typename T>
ostream &print(ostream &o, const T &s)
{
return o << s << endl;
}
template <typename A, typename... T>
ostream &print(ostream &o, const A &a, const T &... s)
{
o << a << ",";
return print(o, s...);
}
template <typename... T>
ostream &err_msg(ostream &o = std::cout, const T &... s)
{
// 这里会展开 debug_rep(int) debug_rep(char)
return print(o, debug_rep(s)...);
}
int main(int argc, char const *argv[])
{
err_msg_old({"1", "2", "3"});
err_msg(std::cout, "1", 1, 3.14);
while (1)
;
return 0;
}
16.57
可变参数的比较灵活,但是要用比较多的堆栈估计
简单版本的,代码简单,方便调试
16.58
// 16.58 你的 StrVec 类及你为16.1.2节练习中编写的 Vec 类添加 emplace_back 函数。
// template< class... Args >
// void emplace_back( Args&&... args );
/*
template <class... Args>
T *emplace_back(Args &&... args)
{
chk_n_alloc();
alloc.construct(first_free++, std::forward<Args>(args)...);
}
std::string its = "my_name_is_yer";
vec.emplace_back(its.begin(), its.end());
vec.emplace_back("back_123");
vec.emplace_back(20, ‘c‘);
*/
// 16.16 定义模版类的vec 替代之前的Strvec
#include <memory>
#include <algorithm>
#include <utility>
#include <initializer_list>
#include <exception>
#include <string>
#include <stdexcept>
#include <iostream>
// using std::cin;
// using std::cout;
// using std::endl;
// 定义一些公共操作
// 从一个迭代器范围复制 到指定的位置,返回内存的首地址和尾地址+1 也就是返回 begin和end
/**
如何设计一个 vector
1. 首先我们定义一个分配器,一个首地址,尾地址,以及容量
2. 提供 size,begin,end,capacity,at,[]
3. push_back
chk_n_alloc();
3.1 检查容量的操作
3.2 容量的分配,这里需要设计一个移动函数
alloc.construct
3.3 数据构造
4. 移动函数涉及
alloc_n_move
4.1 内存分配
4.2 原来数据的移动
5.设计reserve
5.1 内存分配和移动 alloc_n_move
6.设计resize
重设容器大小以容纳 count 个元素。
若当前大小大于 count ,则减小容器为其首 count 个元素。
设置指针,调用元素的析构函数 destroy析构在已分配存储中的对象
若当前大小小于 count ,则后附额外元素,并以 value 的副本初始化。
也就是调用reserve
*/
template <typename>
class Vec;
template <typename T>
bool operator==(const Vec<T> &, const Vec<T> &);
template <typename T>
bool operator!=(const Vec<T> &, const Vec<T> &);
template <typename T>
bool operator<(const Vec<T> &, const Vec<T> &);
template <typename T>
bool operator>(const Vec<T> &, const Vec<T> &);
template <typename T>
bool operator<=(const Vec<T> &, const Vec<T> &);
template <typename T>
bool operator>=(const Vec<T> &, const Vec<T> &);
template <typename T>
class Vec
{
friend bool operator==<T>(const Vec<T> &, const Vec<T> &);
friend bool operator!=<T>(const Vec<T> &, const Vec<T> &);
// clang-format off
friend bool operator< <T>(const Vec<T>&, const Vec<T>&);
friend bool operator> <T>(const Vec<T>&, const Vec<T>&);
// clang-format on
friend bool operator<=<T>(const Vec<T> &, const Vec<T> &);
friend bool operator>=<T>(const Vec<T> &, const Vec<T> &);
private:
T *elements;
T *first_free;
T *cap;
std::allocator<T> alloc;
std::pair<T *, T *> alloc_n_copy(const T *b, const T *e);
void range_initialize(const T *first, const T *last);
void free();
void reallocate();
void alloc_n_move(size_t new_cap);
void chk_n_alloc()
{
if (size() == capacity())
reallocate();
}
public:
Vec(std::initializer_list<T>);
Vec() : elements(nullptr), first_free(nullptr), cap(nullptr) {}
Vec(const Vec<T> &);
//------------------------------------------------------------Vec<T> &operator=(const Vec<T> &s);
Vec &operator=(const Vec<T> &s);
Vec(Vec<T> &&) noexcept;
Vec &operator=(Vec<T> &&) noexcept;
T *begin() const { return elements; }
T *end() const { return first_free; }
T &at(size_t pos) { return *(elements + pos); }
const T &at(size_t pos) const { return *(elements + pos); }
T &operator[](size_t n) { return elements[n]; }
const T &operator[](size_t n) const { return elements[n]; }
void push_back(const T &);
size_t size() const { return first_free - elements; }
size_t capacity() const { return cap - elements; }
void reserve(size_t new_cap);
void resize(size_t count);
void resize(size_t count, const T &);
template <class... Args>
T *emplace_back(Args &&... args)
{
chk_n_alloc();
alloc.construct(first_free++, std::forward<Args>(args)...);
}
~Vec();
};
template <typename T>
std::pair<T *, T *> Vec<T>::alloc_n_copy(const T *b, const T *e)
{
auto data = alloc.allocate(e - b);
return {data, std::uninitialized_copy(b, e, data)};
}
template <typename T>
void Vec<T>::range_initialize(const T *first, const T *last)
{
auto newdata = alloc_n_copy(first, last);
elements = newdata.first;
first_free = cap = newdata.second;
}
template <typename T>
void Vec<T>::free()
{
if (elements)
{
//执行元素的析构函数
for_each(elements, first_free, [this](T &rhs) { alloc.destroy(&rhs); });
//释放vector内存
alloc.deallocate(elements, cap - elements);
}
}
template <typename T>
Vec<T>::Vec(std::initializer_list<T> il)
{
range_initialize(il.begin(), il.end());
}
template <typename T>
Vec<T>::Vec(const Vec<T> &s)
{
range_initialize(s.begin, s.end);
}
template <typename T>
Vec<T> &Vec<T>::operator=(const Vec<T> &s)
{
if (this != s)
{
auto data = alloc_n_copy(s.begin(), s.end());
free();
elements = data.first;
first_free = cap = data.second;
}
return *this;
}
template <typename T>
Vec<T>::Vec(Vec<T> &&s) noexcept
{
elements = s.elements;
first_free = s.first_free;
cap = s.cap;
s.elements = s.first_free = s.cap = nullptr;
}
template <typename T>
Vec<T> &Vec<T>::operator=(Vec<T> &&s) noexcept
{
if (this != &s)
{
free();
elements = s.elements;
first_free = s.first_free;
cap = s.cap;
s.elements = s.first_free = s.cap = nullptr;
}
return *this;
}
template <typename T>
Vec<T>::~Vec()
{
free();
}
template <typename T>
void Vec<T>::alloc_n_move(size_t new_cap)
{
auto newdata = alloc.allocate(new_cap);
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 + new_cap;
}
template <typename T>
void Vec<T>::reallocate()
{
auto newcapacity = size() ? 2 * size() : 1;
alloc_n_move(newcapacity);
}
template <typename T>
void Vec<T>::reserve(size_t new_cap)
{
if (new_cap <= capacity())
return;
alloc_n_move(new_cap);
}
// resize
// 重设容器大小以容纳 count 个元素。
// 若当前大小大于 count ,则减小容器为其首 count 个元素。
// 若当前大小小于 count ,则后附额外元素,并以 value 的副本初始化。
template <typename T>
void Vec<T>::resize(size_t count, const T &s)
{
if (count > size())
{
if (count > capacity())
reserve(count * 2);
for (size_t i = size(); i != count; ++i)
alloc.construct(first_free++, s);
}
else if (count < size())
{
while (first_free != elements + count)
alloc.destroy(--first_free);
}
}
template <typename T>
void Vec<T>::resize(size_t count)
{
resize(count, T());
}
template <typename T>
void Vec<T>::push_back(const T &s)
{
chk_n_alloc();
alloc.construct(first_free++, s);
}
//------------------------------------------------------------------------------
//------------------------------------------------------------------------------
//------------------------------------------------------------------------------
template <typename T>
bool operator==(const Vec<T> &lhs, const Vec<T> &rhs)
{
return (lhs.size() == rhs.size() &&
std::equal(lhs.begin(), lhs.end(), rhs.begin()));
}
template <typename T>
bool operator!=(const Vec<T> &lhs, const Vec<T> &rhs)
{
return !(lhs == rhs);
}
template <typename T>
bool operator<(const Vec<T> &lhs, const Vec<T> &rhs)
{
return std::lexicographical_compare(lhs.begin(), lhs.end(), rhs.begin(),
rhs.end());
}
template <typename T>
bool operator>(const Vec<T> &lhs, const Vec<T> &rhs)
{
return rhs < lhs;
}
template <typename T>
bool operator<=(const Vec<T> &lhs, const Vec<T> &rhs)
{
return !(rhs < lhs);
}
template <typename T>
bool operator>=(const Vec<T> &lhs, const Vec<T> &rhs)
{
return !(lhs < rhs);
}
int main()
{
Vec<std::string> vec;
vec.reserve(6);
std::cout << "capacity(reserve to 6): " << vec.capacity() << std::endl;
vec.reserve(4);
std::cout << "capacity(reserve to 4): " << vec.capacity() << std::endl;
vec.push_back("hello");
vec.push_back("world");
vec.resize(4);
std::string its = "my_name_is_yer";
vec.emplace_back(its.begin(), its.end());
vec.emplace_back("back_123");
vec.emplace_back(20, ‘c‘);
for (auto i = vec.begin(); i != vec.end(); ++i)
std::cout << *i << std::endl;
std::cout << "-EOF-" << std::endl;
vec.resize(1);
for (auto i = vec.begin(); i != vec.end(); ++i)
std::cout << *i << std::endl;
std::cout << "-EOF-" << std::endl;
Vec<std::string> vec_list{"hello", "world", "pezy"};
for (auto i = vec_list.begin(); i != vec_list.end(); ++i)
std::cout << *i << " ";
std::cout << std::endl;
// Test operator==
const Vec<std::string> const_vec_list{"hello", "world", "pezy"};
if (vec_list == const_vec_list)
for (const auto &str : const_vec_list)
std::cout << str << " ";
std::cout << std::endl;
// Test operator<
const Vec<std::string> const_vec_list_small{"hello", "pezy", "ok"};
std::cout << (const_vec_list_small < const_vec_list) << std::endl;
// Test []
std::cout << const_vec_list_small[1] << std::endl;
while (1)
;
}
16.59
// 假定 s 是一个 string,解释调用 svec.emplace_back(s)会发生什么
1.转发扩展包
2. s 传递是值传递,也就是会调用string newstring(s),拷贝构造函数
16.60
// 解释 make_shared 是如何工作的。
最简单的一种重载是
template< class T, class... Args >
shared_ptr<T> make_shared( Args&&... args );
我们在使用的时候, shared_ptr<string> a=make_shared<string>("123");
1. 转发扩展包给 class T 的构造函数
2. 内部提取地址到shared<T>
16.61
// 解释 make_shared 是如何工作的。
// 最简单的一种重载是
// template< class T, class... Args >
// shared_ptr<T> make_shared( Args&&... args );
// 我们在使用的时候, shared_ptr<string> a=make_shared<string>("123");
// 1. 转发扩展包给 class T 的构造函数
// 2. 内部提取地址到shared<T>
#include <memory>
#include <string>
#include <iostream>
using namespace std;
template <class T, class... Args>
shared_ptr<T> my_make_shared(Args &&... args)
{
shared_ptr<T> ret(new T(std::forward<Args>(args)...));
return ret;
}
int main(int argc, char const *argv[])
{
shared_ptr<string> s1 = my_make_shared<string>("123");
std::cout << *s1 << std::endl;
while (1)
;
return 0;
}
16.62
// 16.62 定义你自己版本的 hash<Sales_data>, 并定义一个 Sales_data 对象的 unorder_multise。将多条交易记录保存到容器中,并打印其内容
// p396
// 无序容器需要提供 元素的== 以及哈希函数
// 需要提供hash<类型>的模版,一个 ()运算返回size_t 表示hash值
#include <string>
#include <vector>
#include <iostream>
#include <unordered_set>
using namespace std;
class Sales_data
{
friend bool operator==(const Sales_data &, const Sales_data &);
friend std::hash<Sales_data>;
private:
string isbn;
unsigned units_sold = 0;
double revenue = 0.0;
public:
Sales_data(string isbn, unsigned units_sold, double revenue) : isbn(isbn), units_sold(units_sold), revenue(revenue) {}
double avg_price() { return revenue / units_sold; }
void print() { std::cout << "name=" << isbn << " aver_price= " << avg_price() << std::endl; }
string prints() { return string("name=") + isbn + " aver_price= " + to_string(avg_price()); }
};
bool operator==(const Sales_data &a, const Sales_data &b)
{
return (a.isbn == b.isbn) && (a.revenue == b.revenue) && (a.units_sold == b.units_sold);
}
namespace std
{
template <>
struct hash<Sales_data>
{
typedef size_t result_type;
typedef Sales_data argument_type;
size_t operator()(const Sales_data &s) const
{
return hash<string>()(s.isbn) ^
hash<unsigned>()(s.units_sold) ^
hash<double>()(s.revenue);
}
};
} // namespace std
int main(int argc, const char **argv)
{
Sales_data A1("A1", 5, 17);
A1.print();
std::unordered_multiset<Sales_data> mset;
mset.emplace(A1);
mset.emplace("C++ Primer", 5, 9.99);
for (auto ch : mset)
std::cout << "hash()=" << std::hex << std::hash<Sales_data>()(ch) << ch.prints() << std::endl;
while (1)
;
return 0;
}
16.63
16.64
// 16.64 为上一题的模版编写特例化版本来处理vector<const char*>。编写程序使用这个特例化版本。
// 16.63 定义一个函数模版,统计一个给定值在一个vecor中出现的次数。
// 测试你的函数,分别传递给它一个double的vector,一个int的vector以及一个string的vector。
#include <algorithm>
#include <string>
#include <iostream>
#include <vector>
using namespace std;
template <typename T>
int GetCount(T elem, const vector<T> &v)
{
std::cout << __LINE__ << " :GetCount(T elem, const vector<T> &v)" << std::endl;
int i = 0;
for (auto ch : v)
{
if (ch == elem)
i++;
}
return i;
}
template <>
int GetCount(const char *elem, const vector<const char *> &v)
{
std::cout << __LINE__ << " :GetCount(const char *elem, const vector<const char *> &v)" << std::endl;
int i = 0;
for (auto ch : v)
{
if (string(ch) == string(elem))
i++;
}
return i;
}
int GetCount(const char *elem, const vector<string> &v)
{
std::cout << __LINE__ << " :GetCount(const char *elem, const vector<string> &v)" << std::endl;
return GetCount(string(elem), v);
}
int GetCount(const int elem, const vector<double> &v)
{
return GetCount((double)elem, v);
}
int main(int argc, char const *argv[])
{
vector<string> s({"123", "456", "789", "123", "222"});
std::cout << GetCount("123", s) << std::endl;
std::cout << GetCount(string("123"), s) << std::endl;
vector<int> i = {1, 2, 3, 4, 5, 6, 1, 2};
std::cout << GetCount(1, i) << std::endl;
// vector<double> d = {1.0, 2.5, 3, 4, 5, 6, 1, 2};
// std::cout << GetCount(1, d) << std::endl;
vector<const char *> s3({"123", "456", "789", "123", "222"});
std::cout << GetCount("123", s3) << std::endl;
while (1)
;
return 0;
}
16.65
// 在16.3节中我们定义了两个重载的 debug_rep 版本,一个接受 const char* 参数,另一个接受 char * 参数。将这两个函数重写为特例化版本。
// 16.48 编写你自己版本的 debug_rep 函数。
#include <iostream>
#include <string>
#include <sstream>
using std::string;
string debug_rep(const string &t)
{
return "String:" + t;
}
template <typename T>
string debug_rep(const T &t)
{
std::ostringstream ret;
ret << t;
return ret.str();
}
template <typename T>
string debug_rep(T *t)
{
std::ostringstream ret;
ret << "Point Addr=" << t;
if (t == nullptr)
ret << "nullptr";
else
ret << debug_rep(*t);
return ret.str();
}
template <>
string debug_rep(const char *t)
{
return string("const char*") + debug_rep(string(t));
}
template <>
string debug_rep(char *t)
{
return "char*" + debug_rep(string(t));
}
// string debug_rep(const char *t)
// {
// return "const char*" + debug_rep(string(t));
// }
int main(int argc, const char **argv)
{
std::cout << debug_rep("123456") << std::endl;
while (1)
;
return 0;
}
16.66
重载debug_rep 函数与特例化它相比,有何优点和缺点?
特例化是的本质是实例化一个模版,不影响函数匹配. 而重载如果有非模版的,直接使用该版本
重载会改变函数匹配顺序,因为增加了新函数
几个函数都提供同样好的匹配的情况下,编译器会选择非板版本
16.67
定义特例化版本会影响 debug_rep 的函数匹配吗?如果不影响,为什么?
不会改变,特例化模板函数不会重载函数,不会影响函数匹配顺序 也就是不产生新的函数类型
> p262
特例化是的本质是实例化一个模版,不影响函数匹配. 而重载如果有非模版的,直接使用该版本
也就是他的匹配顺序是模版的顺序,模版之后下面来找具体的特例化(**可以理解为先匹配模版后才能匹配特例化**)
而一个重载和一个模版是平等关系的
?```cpp
重载的版本(包括模版)-------------模版
|
|~~~~~~~~~~~~~~~|
特例化 通用的模版实现
以上是关于cppPrimer学习16th的主要内容,如果未能解决你的问题,请参考以下文章