网站首页 > 博客文章 正文
模板访问权限的放宽
当使用嵌套类(定义在类内部的类)进行模板特化或偏特化时,如果这个嵌套类的访问权限是私有或者保护时,按照以前的 C++ 语法标准,是没有权限进行访问的。但这样的代码在开发模板库时是很常用的,通过对嵌套类使用 trait 技巧来实现对不同类型的模板的特定策略。
由于这种用法很常用,所以很多编译器都默许这样的代码编译通过。所以在 C++20 中,对这种用法进行了规范,要么明确允许这种用法,明确不受嵌套类的权限影响,要么是增加模板的友元(不是具体类的友元)。经过评估,友元的方案增加不必要的复杂性,最后还是采用第一种方案,明确允许这种用法,简化代码。
嵌套类的模板特化和偏特化的例子如下:
// C++20 新特性(24):模板访问权限和typename的放宽
#include <iostream>
using std::cout, std::endl;
// <0> 辅助类,用于推导模板参数
template<class T>
struct extract_value_type
{
typedef T value_type;
};
template< template<typename> class X, class T >
struct extract_value_type< X<T> >
{
typedef T value_type;
};
template<class T>
struct trait
{
int f() { return 1; }
int g() { return 2; }
};
class CA
{
private:
class CB
{
public:
int h() { return 10; }
};
template<class U> struct Impl
{
int h() { U u1; return u1.h() + 100; }
};
int f() { return 3; }
public:
int g();
int h() { return 9; }
};
// <1> 模板特化,使用CA::CB私有嵌套类进行特化,C++20明确允许访问
template<> struct trait< CA::CB >
{
// int f() { CA::CB u1; return u1.h() + 4; } // <1-2> Error,可以特化这个嵌套类作为参数的模板类,但不代表在特化实现中可以使用这个嵌套类,除非设置了友元
int f() { typename extract_value_type< trait >::value_type u1; return u1.h() + 4; } // <1-3> OK,通过辅助类推导模板参数,则可以在特化实现中使用私有的嵌套类
int g() { return 5; }
};
// <2> 模板偏特化,使用CA::Impl私有嵌套模板类进行特化,C++20明确允许访问
template<class U>
struct trait< CA::Impl<U> >
{
int f() { CA::Impl<U> u1; return u1.h() + 6; } // <2-2> 偏特化中,可以使用私有的嵌套类模板,只是模板,并没有实例化具体的类
int g() { return 7; }
};
int CA::g()
{
// <3> 使用<1>中的特化类,因为CA的类成员函数可以访问CB
trait< CB > b1;
int c1 = b1.f();
int c2 = b1.g();
// <4> 使用<2>中的特化类,因为CA的类成员函数可以访问Impl模板,也可以访问Impl<CB>这个实例化的类
trait< Impl<CB> > b2;
int c3 = b2.f();
int c4 = b2.g();
printf( "%d %d %d %d\n", c1, c2, c3, c4 ); // 输出 14 5 116 7
return 8;
}
int main( int argc, char * argv[] )
{
CA a;
a.g();
// trait<CA::CB> b; // <5> Error,定义模板特化时可以不管访问权限,但实例化模板时还是要检查访问权限
// trait< CA::Impl<CA> > c; // <6> Error,定义模板偏特化时可以不管访问权限,但实例化模板时还是要检查访问权限
return 0;
}
在特化的模板实现中,不能直接使用模板参数的私有嵌套类,但可以通过模板推导的技巧,间接使用模板参数的私有嵌套类,而在偏特化中,则是可以直接使用的。
但是到了通过这个模板类定义具体的变量时,还是会受到访问权限的限制。
模板中放宽对 typename 的使用
在模板开发中,有时候无法简单地判断一个和模板参数相关的表达式,到底是一个类型还是一个变量,因此需要添加typename关键字来明确说明这个表达式是一个类型。
但有些地方,实际上是只能出现类型的,但按照以前的C++标准,还是要求明确添加typename关键字来说明这个表达式是一个类型,这样造成开发模板时要写太多的typename。因此在C++20中,对这些地方进行放宽,明确只能出现类型的地方,可以不写typename。
另外,对于本地定义的类型,也明确不需要加typename。
具体例子如下:
// C++20 新特性(24):模板访问权限和typename的放宽
#include <iostream>
#include <string>
using std::cout, std::endl;
struct CA
{
typedef std::string TYPE;
};
template<class T>
/* typename */ T::TYPE f1() // <1> 这里明确是需要一个类型的地方,所以不用写typename
{
typename T::TYPE a; // <2> 这里不是必须出现类型的地方,所以需要明确写typename
// T::TYPE * a2; // <3> 不写typename,会将T::TYPE当做一个变量,和a2相乘,
typedef typename T::TYPE TY3; // <4> 这里不是必须出现类型的地方,所以需要明确写typename
TY3 a3; // <5> 本地typedef出来的类型,可以直接使用,不需要加typename
using TY4 = T::TYPE; // <6> 这里明确是需要一个类型的地方,所以不用写typename
TY4 a4;
void g1( typename T::TYPE ); // <7> 这里不是必须出现类型的地方,所以需要明确写typename,表示定义一个函数
// std::string g2( T::TYPE ); // <8> 不加typename,就当做值使用,表示定义一个变量并通过另一个值来初始化,但实际不是一个值而是类型,所以编译失败
return a;
}
int main( int argc, char * argv[] )
{
CA::TYPE a1 = f1<CA>();
return 0;
}
- 上一篇: C++20 Ranges:现代范围操作
- 下一篇: C++ 20新特性之模块
猜你喜欢
- 2025-08-03 C++语法进阶-字符:字符变量(char)
- 2025-08-03 c++26新功能—Read-Copy-Update
- 2025-08-03 为什么Linux之父那么讨厌C++ 他骂的这几点。句句扎心
- 2025-08-03 为什么Linux之父那么讨厌C++ 他骂的这几点!句句扎心
- 2025-08-03 20道qiao牛逼的c++/c面试题
- 2025-08-03 C++学习教程_C++语言随到随学_不耽误上班_0基础
- 2025-08-03 20天轻松入门《C++第四章——函数》——4经坛教育
- 2025-08-03 看完侯捷老师所有C++视频之后的总结
- 2025-08-03 C++11+ 泛型编程(模板)
- 2025-08-03 C++20并发库新成员jthread(续)
你 发表评论:
欢迎- 08-06nginx 反向代理
- 08-06跨表插入连续的日期,sheetsname函数#excel技巧
- 08-06初中生也能学的编程,不走弯路,先用后学
- 08-06find命令的“七种武器”:远不止-name和-type
- 08-06恶意代码常见的编程方式
- 08-06kali2021ping 外网不通
- 08-06因为一个函数strtok踩坑,我被老工程师无情嘲笑了
- 08-06hadoop集群搭建详细方法
- 18℃nginx 反向代理
- 最近发表
- 标签列表
-
- ifneq (61)
- 字符串长度在线 (61)
- googlecloud (64)
- powershellfor (73)
- messagesource (71)
- plsql64位 (73)
- vueproxytable (64)
- npminstallsave (63)
- #NAME? (61)
- promise.race (63)
- 2019cad序列号和密钥激活码 (62)
- window.performance (66)
- qt删除文件夹 (72)
- mysqlcaching_sha2_password (64)
- nacos启动失败 (64)
- ssh-add (70)
- yarnnode (62)
- abstractqueuedsynchronizer (64)
- source~/.bashrc没有那个文件或目录 (65)
- springboot整合activiti工作流 (70)
- jmeter插件下载 (61)
- 抓包分析 (60)
- idea创建mavenweb项目 (65)
- qcombobox样式表 (68)
- pastemac (61)
本文暂时没有评论,来添加一个吧(●'◡'●)