using 声明

先来看 using 声明在类中的应用:

代码1

#include <iostream>

using namespace std;

struct A {
void f(int) {cout << "A::f(int)" << endl;}
}; struct S : A {
}; int main()
{
S s;
s.f(1); // A::f(int)
}
  • 类 S 继承了类 A 的成员函数 f,所以类 S 的实例 s 调用 f 输出为 A::f

代码2

#include <iostream>

using namespace std;

struct A {
void f(int) {cout << "A::f(int)" << endl;}
}; struct S : A {
void f(double) {cout << "S::f(double)" << endl;}
}; int main()
{
S s;
s.f(1); // S::f(double)
}
  • 类 S 继承了类 A 的成员函数 f,同时类 S 也定义了成员函数 f
  • 类 S 的成员函数 f 遮蔽了基类中的同名成员函数,所以 S 的实例 s 调用 f 输出为 S::f

代码3

#include <iostream>

using namespace std;

struct A {
void f(int) {cout << "A::f(int)" << endl;}
}; struct S : A {
using A::f;
void f(double) {cout << "S::f(double)" << endl;}
}; int main()
{
S s;
s.f(1); // A::f(int)
s.f(2.5); // S::f(double)
}
  • 类 S 继承了类 A 的成员函数 f,同时类 S 也定义了成员函数 f
  • 类 S 通过 using 声明将基类 A 的成员函数 f 引入自己的作用域。类 S 的成员函数 f 与基类 A 中的同名成员函数形成重载关系。
  • 参数为整型时,根据重载决策 S 的实例 s 调用 f 输出为 A::f
  • 参数为浮点型时,根据重载决策 S 的实例 s 调用 f 输出为 S::f

代码4

#include <iostream>

using namespace std;

struct A {
void f(int) {cout << "A::f(int)" << endl;}
}; struct B {
void f(double) {cout << "B::f(double)" << endl;}
}; struct S : A, B {
}; int main()
{
S s;
s.f(1); // compile error
s.f(2.5); // compile error
}
  • 类 S 同时继承了类 A 和类 B 的成员函数 f,两者形成竞争关系。
  • 编译器不能判断 S 的实例 s 所调用的成员函数 f 来自类 A 还是类 B,故编译出错。

代码5

#include <iostream>

using namespace std;

struct A {
void f(int) {cout << "A::f(int)" << endl;}
}; struct B {
void f(double) {cout << "B::f(double)" << endl;}
}; struct S : A, B {
using A::f;
using B::f;
}; int main()
{
S s;
s.f(1); // A::f(int)
s.f(2.5); // B::f(double)
}
  • 类 S 同时继承了类 A 和类 B 的成员函数 f。
  • 类 S 通过 using 声明将基类 A 和基类 B 的成员函数 f 都引入自己的作用域。基类 A 和基类 B 的同名成员函数在类 S 中形成重载关系。
  • 参数为整型,根据重载决策 S 的实例 s 调用 f 输出为 A::f
  • 参数为浮点型时,根据重载决策 S 的实例 s 调用 f 输出为 B::f

C++17的 using 声明

在 C++17 中多个 using 声明可以通过逗号连接起来,合成一个 using 声明。

代码6

#include <iostream>

using namespace std;

struct A {
void f(int) {cout << "A::f(int)" << endl;}
}; struct B {
void f(double) {cout << "B::f(double)" << endl;}
}; struct S : A, B {
using A::f, B::f; // C++17
}; int main()
{
S s;
s.f(1); // A::f(int)
s.f(2.5); // B::f(double)
}

C++17的变长 using 声明

通过使用加了 ... 的 using 声明,可以将变长模板参数类型中的 using 声明转化为由多个用逗号合成的变长 using 声明。

#include <iostream>
#include <string> using namespace std; template<class... Ts> struct overloaded : Ts... { using Ts::operator()...; };
template<class... Ts> overloaded(Ts...) -> overloaded<Ts...>; int main()
{
overloaded s{
[](int){cout << "int" << endl;},
[](double){cout << "double" << endl;},
[](string){cout << "string" << endl;},
};
s(1); // int
s(1.); // double
s("1"); // string
}
  • template<class... Ts> struct overloaded : Ts... { using Ts::operator()...; };

    这是一个类模板的声明。
  • template<class... Ts>:overloaded 类的模板参数为可变长的参数包 Ts。

    假设 Ts 包含 T1, T2, ... , TN,那么这一句声明可以展开为:template<class T1, class T2, ..., class TN>
  • struct overloaded : Ts...:overloaded 类的基类为参数包 Ts 内所有的参数类型。

    假设 Ts 包含 T1, T2, ... , TN,那么这一句声明可以展开为:struct overloaded : T1, T2, ..., TN
  • using Ts::operator()...;:这是一个变长 using 声明。

    假设 Ts 包含 T1, T2, ... , TN,那么这一句声明可以展开为:using T1::operator(), T1::operator(), ..., TN::operator();

    也就是说,overloaded 类的基类即参数包 Ts 内所有的参数类型的函数调用操作符均被 overloaded 类引入了自己的作用域。
  • template<class... Ts> overloaded(Ts...) -> overloaded<Ts...>;

    这是一个自动推断向导,用于帮助编译器根据 overloaded 构造器参数的类型来推导 overloaded 的模板参数类型。

    这个自动推断向导告诉编译器,如果 overloaded 构造器所有参数的类型的集合为Ts,那么 overloaded 的模板参数类型就是 Ts 所包含的所有类型。

    也就是说如果表达式 a1, a2, ..., an 的类型分别为 T1, T2, ..., TN,

    那么构造器表达式 overloaded {a1, a2, ..., an} 的类型就是 overloaded<T1, T2, ..., TN>

    * overloaded s{

    [](int){cout << "int" << endl;},

    [](double){cout << "double" << endl;},

    [](string){cout << "string" << endl;},

    };

    overloaded 类的实例 s 的构造器包含3个lambda参数,也可以看作3个各自包含一个 operator() 的函数对象。

    根据 overloaded 类的定义,s 对象将继承这3个lambda(函数对象)的 operator() ,也就是说这3个lambda的 operator() 即函数体在 s 对象内部形成重载关系。

    根据 overloaded 类的自动推断向导,s 对象的类型为overloaded<T1, T2, T3>,其中T1, T2, T3为3个lambda参数的类型。
  • 通过利用 C++17 的新特性变长的 using 声明以及自动推断向导,overloaded类的实例可以简洁并且巧妙地将多个lambda合成一个大的具有多个相互重载的 operator() 的函数对象。
  • overloaded 这个类模板如此有用,实现机制又如此精妙,实在是应该早日纳入标准库中。

C++17尝鲜:变长 using 声明的更多相关文章

  1. C++17尝鲜

    https://cloud.tencent.com/developer/article/1351910 [译]C++17,optional, any, 和 variant 的更多细节 用户261520 ...

  2. C++17尝鲜:variant

    variant variant 是 C++17 所提供的变体类型.variant<X, Y, Z> 是可存放 X, Y, Z 这三种类型数据的变体类型. 与C语言中传统的 union 类型 ...

  3. C++17尝鲜:结构化绑定声明(Structured Binding Declaration)

    结构化绑定声明 结构化绑定声明,是指在一次声明中同时引入多个变量,同时绑定初始化表达式的各个子对象的语法形式. 结构化绑定声明使用auto来声明多个变量,所有变量都必须用中括号括起来. cv-auto ...

  4. C++17尝鲜:在 if 和 switch 语句中进行初始化

    初始化语句 在C++17中,类似于 for 语句,在 if 和 switch 语句的判断条件之前也能加上初始化语句,语法形式如下: if (初始化语句; 条件) 语句 else 语句 switch ( ...

  5. C++17尝鲜:编译期 if 语句

    Constexpr If(编译期 if 语句) 以 if constexpr 打头的 if 语句被称为 Constexpr If. Constexpr If 是C++17所引入的新的语法特性.它为C+ ...

  6. C++17尝鲜:string_view

    string_view string_view 是C++17所提供的用于处理只读字符串的轻量对象.这里后缀 view 的意思是只读的视图. 通过调用 string_view 构造器可将字符串转换为 s ...

  7. C++17尝鲜:类模板中的模板参数自动推导

    模板参数自动推导 在C++17之前,类模板构造器的模板参数是不能像函数模板的模板参数那样被自动推导的,比如我们无法写 std::pair a{1, "a"s}; // C++17 ...

  8. PL/SQL — 变长数组

    PL/SQL变长数组是PL/SQL集合数据类型中的一种,其使用方法与PL/SQL嵌套表大同小异,唯一的区别则是变长数组的元素的最大个数是有限制的.也即是说变长数组的下标固定下限等于1,上限可以扩展.下 ...

  9. C99新增内容之变长数组(VLA)

    我们在使用多维数组是有一点,任何情况下只能省略第一维的长度.比如在函数中要传一个数组时,数组的行可以在函数调用时传递,当属数组的列却只能在能被预置在函数内部.看下面一个例子: #define COLS ...

随机推荐

  1. UI相关教程:HUD、UMG和Widget

    转自:http://aigo.iteye.com/blog/2258612 蓝图脚本来处理 ================================================== 用UM ...

  2. C#深入解析委托——C#中为什么要引入委托

    引言: 对于一些刚接触C# 不久的朋友可能会对C#中一些基本特性理解的不是很深,然而这些知识也是面试时面试官经常会问到的问题,所以我觉得有必要和一些接触C#不久的朋友分享下关于C#基础知识的文章,所以 ...

  3. C语言格式化%整理

    以输出为例: #include <stdio.h> main() { printf("**进制****************************************** ...

  4. Css实战第二天小结

    清除浮动的四种方式: 1.1    给父盒子设置一个高度: 1.2    Clear:both; 1.3    Overflow:hidden; 1.4    使用伪元素 .clearfix:befo ...

  5. GNU coreutils

    内核实用程序,针对文本及文件操作.涉及到102条linux命令.命令列表:cp.install.ln.mv.ls.echo…… 常见选项 1.退出状态 2.备份选项 cp, install, ln, ...

  6. ORA-00257: archiver error. Connect internal only, until freed【日志归档清理】

    select * from V$FLASH_RECOVERY_AREA_USAGE;  查看使用情况 用plsql登陆时提示“ORA-00257: archiver error. Connect in ...

  7. linux环境下redis数据库的安装|配置|启动

    安装 下载:打开redis官方网站,推荐下载稳定版本(stable) 解压 tar zxvf redis-3.2.5.tar.gz 复制:推荐放到usr/local目录下 sudo mv -r red ...

  8. 30.纯 CSS 创作一个晃动的公告板

    原文地址:https://segmentfault.com/a/1190000014983030 感想: 绝对定位+动画 HTML代码: <div class="signboard&q ...

  9. ant 标签详解

    Ant 开发 Ant的构建文件当开始一个新的项目时,首先应该编写Ant构建文件.构建文件定义了构建过程,并被团队开发中每个人使用.Ant构建文件默认命名为build.xml,也可以取其他的名字.只不过 ...

  10. EXCEL中统计单元格内容出现次数

    参考网站: https://jingyan.baidu.com/article/7c6fb428dfcc9580642c90ae.html 统计单元格内容出现次数是工作中经常会涉及到的问题. 那么,如 ...