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. 高可用hadoop的hdfs启动的时候namenode启动不了

    启动的时候,一直要求输入namenode密码: 查看namenode的日志如下: 2019-03-28 18:38:08,961 INFO org.apache.hadoop.ipc.Client: ...

  2. androidstudio在创建new project时,窗口太大,看不到下面确定按钮的解决方法

    点击File-->setting-->Appearance将里面的Override default fonts by(not recommended)打钩去掉. 这个是目前找到唯一办法.

  3. React 懒加载组件

    //组件第一次初始化的时候加载. import React, {PropTypes} from 'react'; //import AppComposer from './views/App/AppC ...

  4. 使用SecureCRT软件运维的配置习惯

    使用 SecureCRT 软件运维的配置习惯 作者:Eric 微信:loveoracle11g 选项--->全局选项 配置文件存放路径 默认CRT配置的配置文件在C盘 常规--->默认会话 ...

  5. CRM2016客户端调试

  6. root用户以修改文件的方式,修改用户密码

    老早就听说linux的用户密码只是存储在普通的文本文件里,但一直没去了解,今天遇上了需要强行修改的情况... 给碰到个奇葩linux,居然没有passwd命令. 生成密码摘要 openssl pass ...

  7. MySQL存储过程中使用SELECT …INTO语句为变量赋值

    使用SELECT …INTO语句为变量赋值 在MySQL存储过程中,可以使用SELECT …INTO语句对变量进行赋值,该语句在数据库中进行查询,并将得到的结果赋值给变量.SELECT …INTO语句 ...

  8. ubuntu 16.04 ssh免密码连接不上

    我们在用ubuntu系统搭建集群的时候,配置免密码登录是必须经过的一步 我这里集群的每个节点都是采用的是root用户,因为ubuntu系统不同centos,ubuntu真的限制太多太不友好了 下面看看 ...

  9. GitHub万星的ML算法面试大全

    项目地址:https://github.com/imhuay/Algorithm_Interview_Notes-Chinese 如下所示为整个项目的结构,其中从机器学习到数学主要提供的是笔记与面试知 ...

  10. fastclick.js解决移动端(ipad)点击事件反应慢问题

    参考http://blog.csdn.net/xjun0812/article/details/64919063 http://www.jianshu.com/p/16d3e4f9b2a9 问题的发现 ...