为模板类声明友元类
有时我们需要A类访问模板类B的私有成员,但是不想其他类访问,就要在模板类B里为A类声明友元。比如我们想要实现一个BlobPtr类,让BlobPtr类成为Blob类的友元,这样BlobPtr类就可以访问Blob类了。对于Blob类的声明和定义在前文已经阐述https://llfc.club/articlepage?id=28Vv7hro3VVMPDepLTlLRLqYJhJ。 我们省略Blob类的详细声明,只为它添加友元类BlobPtr类,并且为他添加友元函数operator==
class Blob
{
public:
typedef T value_type;
typedef typename std::vector<T>::size_type size_type;
friend class BlobPtr<T>;
friend bool operator==(const Blob<T> &, const Blob<T> &);
};
实现友元类BlobPtr
接下来我们实现友元类BlobPtr,先对其进行声明
template <typename T>
class BlobPtr
{
public:
BlobPtr() : curr(0) {}
BlobPtr(Blob<T> &a, size_t sz = 0) : wptr(a.data), curr(sz) {}
BlobPtr &operator++();
BlobPtr &operator++(int);
private:
std::shared_ptr<std::vector<T>>
check(std::size_t, const std::string &) const;
std::size_t curr;
std::weak_ptr<std::vector<T>> wptr;
};
在实现其定义,这里只举例实现一部分函数,其余的读者自己实现即可。
template <typename T>
BlobPtr<T> &BlobPtr<T>::operator++()
{
this->curr++;
return *this;
}
template <typename T>
BlobPtr<T> &BlobPtr<T>::operator++(int)
{
BlobPtr &rt = *this;
this->curr++;
return rt;
}
对于友元函数operator == 的定义可以按照如下实现
template <typename T>
bool operator==(const Blob<T> &b1, const Blob<T> &b2)
{
if (b1.size() > b2.size())
{
return true;
}
if (b1.siz() < b2.size())
{
return false;
}
for (unsigned int i = 0; i < b1.size(); i++)
{
if (b1.data[i] == b2.data[i])
{
continue;
}
return b1.data[i] > b2.data[i];
}
return true;
}
模板类的友元还有一些特殊的用法,如下,读者可以自己体会
template <typename T>
class Pal
{
};
template <typename T>
class Pal2
{
};
class C
{
friend class Pal<C>;
template <typename T>
friend class Pal2;
};
template <typename T>
class C2
{
friend class Pal<T>;
template <typename X>
friend class Pal2;
friend class Pal3;
};
定义模板类别名
我们可以通过typedef和using等方式为一个模板类定义别名
template <typename Type>
class Bar
{
friend Type;
};
typedef long long INT64;
typedef Bar<int> mytype;
template <typename T>
using twin = pair<T, T>;
twin<string> authors;
twin<int> infos;
template <typename T>
using partNo = pair<T, unsigned>;
partNo<string> books;
类模板的静态成员
对于类模板的静态成员,其初始化要放在声明的.h文件中。
template <typename T>
class Foo
{
public:
static std::size_t count() { return ctr; }
private:
static std::size_t ctr;
};
template <typename T>
size_t Foo<T>::ctr = 0;
模板类的作用域访问
默认情况下,C++语言假定通过作用域运算符访问的名字不是类型。 因此,如果我们希望使用一个模板类型参数的类型成员,就必须显式告诉编译器该名字是一个类型。 我们通过使用关键字typename来实现这一点:
template <typename T>
typename T::value_type top(const T &c)
{
if (!c.empty())
return c.back();
else
return typename T::value_type();
}
我们定义了一个名为top的模板函数,通过T::value_type声明其返回类型,但是C++默认作用域下value_type是一个成员, 所以为了说明value_type是一个类型就需要用typename关键字做声明。
通用的函数对象
我们可以通过模板类实现通用的仿函数,也就是实现通用的函数对象,我们先实现一个DebugDelete类,用来删除各种类型的指针对象
class DebugDelete
{
public:
DebugDelete(std::ostream &s = std::cerr) : os(s) {}
template <typename T>
void operator()(T *p) const
{
os << "deleting unique_str" << std::endl;
delete p;
}
private:
std::ostream &os;
};
DebugDelete构造函数接纳一个输出流,用来在operator()调用时输出删除信息 接下来我们实现一个测试函数,用来说明DebugDelete的用法
void use_debugdel()
{
double *p = new double;
DebugDelete d;
d(p);
int *np = new int;
DebugDelete()(np);
unique_ptr<int, DebugDelete> p3(new int, DebugDelete());
unique_ptr<string, DebugDelete> sp(new string, DebugDelete());
}
可以看出DebugDelete可以用来给智能指针作删除器用。
尾置类型的推断
C11新标准中提出了尾置类型推断
auto func(int i) -> int (*)[10];
利用这个特性,我们可以用在模板函数中,同样实现一个尾置类型推断函数
template <typename It>
auto fcnrf(It beg, It end) -> decltype(*beg)
{
return *beg;
}
fcnrf的返回类型时It指向元素的解引用(*beg)类型,通过decltype类型推断给出返回的类型。 我们也可以实现一个返回值类型的函数,去掉引用类型。
template <typename It>
auto fcncp(It beg, It end) -> remove_reference<decltype(*beg)>
{
return *beg;
}
引用折叠规则
我们可以用模板定义一个左值引用
template <typename T>
void f1(T &t)
{
}
当我们用模板类型定义一个右值引用时,传递给该类型的实参类型,会根据C++标准进行折叠。 我们先声明一个右值引用的模板函数
template <typename T>
void f2(T &&t)
{
}
如果我们调用f2(42), T就被推断为int int i = 100; f2(i) T就被推断为int& 进行参数展开参数就变为int& && 折叠后就变为int & 所以我们可以做如下归纳: 当模板函数的实参是一个T类型的右值引用 1 传递给该参数的实参是一个右值时, T就是该右值类型 2 传递给该参数的实参是一个左值时, T就是该左值引用类型。
//折叠规则 //X&& ,X&&& 都会被折叠为X& //X&& && 会被折叠为X&& 所以根据这个规律,我们可以实现一个类似于stl的move操作
template<typename T>
typename remove_reference<T>::type && my_move(T&& t){
return static_cast<typename remove_reference<T>::type &&>(t);
}
如果我们在函数中作如下调用
void use_tempmove()
{
int i = 100;
my_move(i);
auto rb = my_move(43);
}
总结
这篇文章介绍了模板参数类型的折叠规则和友元类的声明和使用。 视频链接https://www.bilibili.com/video/BV1cF41177hK?vd_source=8be9e83424c2ed2c9b2a3ed1d01385e9 源码链接 https://gitee.com/secondtonone1/cpplearn
|