C++17尝鲜:变长using声明

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 遮蔽了基类中的同名函数 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 继承了类 A 的成员函数 f,同时类 S 也定义了成员函数 f
  • 类 S 通过 using 声明将基类 A 的成员函数 f 引入自己的作用域。类 S 的成员函数 f 与基类 A 的同名函数形成重载关系。
  • 参数为整型,所以 S 的实例 s 调用 f 输出为 A::f

代码4

#include <iostream>

using namespace std;

struct A {
    void f(int) {cout << "A::f(int)" << endl;}
};

struct B {
    void f(double) {cout << "S::f(double)" << endl;}
};

struct S : A, B {
};

int main()
{
    S s;
    s.f(1); // 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 << "S::f(double)" << endl;}
};

struct S : A, B {
    using A::f;
    using B::f;
};

int main()
{
    S s;
    s.f(1); // A::f(int)
}
  • 类 S 同时继承了类 A 和类 B 的成员函数 f。
  • 类 S 通过 using 声明将基类 A 和基类 B 的成员函数 f 都引入自己的作用域。基类 A 和基类 B 的同名函数在类 S 中形成重载关系。
  • 参数为整型,所以 S 的实例 s 调用 f 输出为 A::f

C++17的 using 声明

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

代码6

#include <iostream>

using namespace std;

struct A {
    void f(int) {cout << "A::f(int)" << endl;}
};

struct B {
    void f(double) {cout << "S::f(double)" << endl;}
};

struct S : A, B {
    using A::f, B::f; // C++17
};

int main()
{
    S s;
    s.f(1); // A::f(int)
}

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 这个模板类如此有用,实现机制又如此复杂,实在是应该早日纳入标准库中。

原文地址:https://www.cnblogs.com/zwvista/p/9256655.html

时间: 2024-11-06 15:27:42

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

C++17尝鲜:string_view

string_view string_view 是C++17所提供的用于处理只读字符串的轻量对象. 通过调用 string_view 构造器可将字符串转换为 string_view 对象. string_view通常用于函数参数类型,可用来取代 const char* 和 const string&. string_view的成员函数即对外接口与 string 相类似,但只包含读取字符串内容的部分. string_view字面量的后缀是 sv.(string字面量的后缀是 s) 实例 #incl

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

我们在使用多维数组是有一点,任何情况下只能省略第一维的长度.比如在函数中要传一个数组时,数组的行可以在函数调用时传递,当属数组的列却只能在能被预置在函数内部.看下面一个例子: #define COLS 4 int sum2d(int ar[][COLS],int rows) { int r; int c; int tot=0; for(r=0;r<rows;r++) for(c=0;c<COLS;c++) tot+=ar[r][c]; return tot; } 现在假设定义了如下数组: in

C++11变长模板解析(深入理解C++11)

参考自:深入理解C++11 变长模版: 变长函数和变长的模版参数 变长函数: double sum(int n, ...)//求n个double数据之和 { double sum = 0; va_list args;//接受输入数据的数据结构,需声明stdarg.h, va_start(args, n); //初始化数据 while (n>0) { sum += va_arg(args, double); //将args中的数据一一取出,每隔sizeof(double)取一次数,再求和 --n;

屌丝就爱尝鲜头——java8总结晒一晒

前两节讨论了那么多,这节就是两个议题,讨论了新增的日期的api,再说一说我的Java8的一些心得体会了. 首先,我们必须要搞清楚Java 8 为什么要增加新的日期的api,这是由于老的日期api非常的繁琐,使用起来非常不方便,Java作者奉行这变者通不变者死的原则,于是增加了这些api.下面,我们总点介绍这几个类--LocalDate类.LocalTime类.LocalDateTime类.DateTimeFormatter类,zoneDate类.一个个来看: Ⅰ.LocalDate类--返回日期

C语言变长数组data[0]

转载:http://www.cnblogs.com/Anker/p/3744127.html 1.前言 今天在看代码中遇到一个结构中包含char data[0],第一次见到时感觉很奇怪,数组的长度怎么可以为零呢?于是上网搜索一下这样的用法的目的,发现在linux内核中,结构体中经常用到 data[0].这样设计的目的是让数组长度是可变的,根据需要进行分配.方便操作,节省空间. 2.data[0]结构 经常遇到的结构形状如下: struct buffer { int data_len; //长度

变长数组(variable-length array,VLA)

处理二维数组的函数有一处可能不太容易理解,数组的行可以在函数调用的时候传递,但是数组的列却只能被预置在函数内部.例如下面这样的定义: 1 #define COLS 4 2 int sum3d(int ar[][COLS], int rows) 3 { 4 int r, c, tot; 5 tot = 0; 6 7 for(r = 0; r < rows; r++) 8 for(c = 0; c < COLS; c++) 9 tot += ar[r][c]; 10 return tot; 11

C语言--变长参数

一.  实现原理 首先变长参数的实现依赖于cdecl调用,因为其规定了出栈方为函数调用方,从而解决被调用函数无法确定参数个数,其次cdecl规定参数入栈顺序为从右到左.所以第一个不定参数位于栈顶 二. 宏源码讲解  (va ---> variable-argument(可变参数)) 头文件 stdarg.h 2.1 va_list #define va_list char * 定义了一个指针arg_ptr, 用于指示可选的参数. 2.2 va_start(arg_ptr, argN) #defi

Java语法糖初探(三)--变长参数

变长参数概念 在Java5 中提供了变长参数(varargs),也就是在方法定义中可以使用个数不确定的参数,对于同一方法可以使用不同个数的参数调用.形如 function(T -args).但是需要明确的一点是,java方法的变长参数只是语法糖,其本质上还是将变长的实际参数 varargs 包装为一个数组. 看下面的例子: 12345678910111213 public class VariVargs { public static void main(String []args) { tes

读书笔记:c语言标准库 - 变长参数

· 变长参数(stdarg.h) 变长参数是c语言的特殊参数形式,例如如下函数声明: int printf(const char * format,...); 如此的声明表明,printf函数除了第一个参数类型为const char*之外,其后可以追加任意数量.任意类型的参数. 在函数实现部分,可以使用stdarg.h里的多个宏来访问各个额外的参数:假设lastarg是变长参数函数的最后一个具名参数(printf里的format),那么在函数内容定义类型为va_list的变量: va_list