Using
声明被扩展了,可以用来声明逗号分隔一组声明的列表。例如,现在可以这样写代码:
class Base {
public:
void a();
void b();
void c();
};
class Derived :private Base {
public:
using Base::a, Base::b, Base::c;
};
C++17之前,需要用三次using
声明。
使用可变的using
声明
逗号分隔的using
声明提供了一种能力,可以泛化地从一组可变数量的基类中派生出相同类型的操作。这项技术特别酷的一个应用是创建一组lambda表达式的重载。定义如下:
tmpl/overload.hpp
// ”继承”所有传入的基类的operator():
template<typename... Ts>
struct overload : Ts...
{
using Ts::operator()...;
};
// 基类型从传入的参数自动推导:
template<typename... Ts>
overload(Ts...) -> overload<Ts...>;
可以如下重载两个lambda表达式:
auto twice = overload {
[](std::string& s) { s += s; },
[](auto& v) { v *= 2; }
};
这里我们创建了一个类型overload
的对象,当我们使用推导指南去推导作为基模板类型overload
的lambda表达式的类型,使用聚合初始化去初始化拥有闭包类型的拷贝构造函数的基类的子对象,每个lambda都是如此。然后using
声明为每个类型overload
生成operator()
。如果没有using
声明,基类就会为同一个operator()
生成两个不同的重载,这就具有歧义了。结果是,可以传一个string
,调用第一个重载函数,或传另一个类型,(只要提供了operator*=
可用)调用第二个重载函数:
int i = 42;
twice(i);
std::cout << "i: " << i << '\n';
std::string s = "hi";
twice(s);
std::cout << "s: " << s << '\n';
// 打印: 84
// 打印: hihi
这项技术的一种应用是std::variant
visitors。
继承构造函数的可变using
声明
与继承构造函数声明一样,现在还允许这样:可以声明一个可变类模板Multi
,从每个它传来的基类派生:
tmpl/using2.hpp
template<typename T>
class Base {
T value{};
public:
Base() {
...
}
Base(T v) : value{v} {
...
}
...
};
template<typename... Types>
class Multi :private Base<Types>...
{
public:
// derive all constructors:
using Base<Types>::Base...;
...
};
针对所有基类构造函数的using
声明,可以为每个类型对应的构造函数派生。现在,当为3个不同类型声明Multi<>
类型:
using MultiISB = Multi<int,std::string,bool>;
可以使用每个对应的构造函数声明对象:
MultiISB m1 = 42;
MultiISB m2 = std::string("hello");
MultiISB m3 = true;
通过新的语言规则,每个初始化调用了匹配到的基类对应的构造函数或者所有其他基类的缺省构造函数。因此
MultiISB m2 = std::string("hello");
为Base<int>
调用了缺省构造函数,string
的构造函数用于Base<std::string>
,为Base<bool>
使用缺省构造函数。原理上讲,还可以在Multi<>
使能所有赋值操作符:
template<typename... Types>
class Multi :private Base<Types>...
{
...
// 派生所有operator=:
using Base<Types>::operator=...;
};