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. echarts折现图配置

    js引用和div容器 <div id="container" style="height: 100%"></div> <scrip ...

  2. 2012 - AD FSMO操作主机角色 -- 作用

    林范围:(林中唯一) 架构主机角色(Schema Master) 定义所有域对象属性 域命名主机角色(Domain Naming Master) 控制域林内域的添加或删除(必须为GC)   域范围:( ...

  3. c#类的继承与包含的关系

    基础例子 class Dept { private string name; private Emp emp; public string getName() { return this.name; ...

  4. sso CAS

    sso:single sign on,在多个应用系统中,用户只需要登陆一次就可以访问所有相互信任的应用系统 CAS框架:Central Authentication Service是实现sso单点登录 ...

  5. 第9课 基于范围的for循环

    1. 基于范围的for循环(range-based for) (1)语法:for(decl : coll){//statement} ①decl用于声明元素及类型,如int elem或auto ele ...

  6. Android WebView清空缓存

    Android原生和H5混合开发,要求用户退出登录后清空H5所有的缓存: 1.清空Cookie CookieSyncManager.createInstance(context.getApplicat ...

  7. SP1812 LCS2 - Longest Common Substring II

    能匹配上子串的节点对它的所有parent都有贡献 在树上转移即可 #include<cstdio> #include<algorithm> #include<cstrin ...

  8. 在同一个Linux上配置多个git账户

    1.首先在~/.ssh目录下执行 ssh-keygen -t rsa -C "miaoying.new@qq.com" 其中 -C "miaoying.new@qq.co ...

  9. 关于python无法显示中文的问题:SyntaxError: Non-ASCII character '\xe4' in file test.py on line 3, but no encoding declared。

    [已解决]关于python无法显示中文的问题:SyntaxError: Non-ASCII character '\xe4' in file test.py on line 3, but no enc ...

  10. 关于 Thread.currentThread()

    currentThread()  到底是什么? 其实currentThread() 只是Thread 的一个静态方法.返回的正是执行当前代码指令的线程引用: /** * Returns a refer ...