目录
一、 using声明符
using 声明,将别处定义的名字引入到此 using 声明所出现的声明区中,其语法形式如下:
/*
*typename-在using声明从基类向类模板中引入成员类型时,可能需要用关键词typename解决待决名
*嵌套名说明符-名字与作用域解析运算符::的序列,以作用域解析运算符结尾。单个::指代全局命名空间。
*无限定标识-标识表达式
*/
using typename(可选) 嵌套名说明符 无限定标识; //(C++17 前)
/*
*声明符列表-一个或多个形式为typename(可选) 嵌套名说明符无限定标识的声明符的逗号分隔列表。某些或所
*有声明符都可以后随省略号 ... 以指示包展开
*/
using 声明符列表; //(C++17 起)
using 声明可以用于将命名空间成员引入到另一命名空间与块作用域,或将基类成员引入到派生类定义中,或将枚举项引入命名空间、块或类作用域中 (C++20 起)。包含多于一个 using 声明符的 using 声明等价于一个对应个数的包含单个 using 声明符的 using 声明序列。 (C++17 起)
例如在派生类通过using引入基类构造函数,不继承默认实参,其签名与基类中的构造函数命名相匹配的构造函数(包括隐式的)候选继承,在派生类中隐式声明。
struct B {
B(int = 13, int = 42);
};
struct D : B {
using B::B;
// 继承构造函数候选集是
// 1. B(const B&)
// 2. B(B&&)
// 3. B(int = 13, int = 42)
// 4. B(int = 13)
// 5. B()
// D 有下列构造函数:
// 1. D()
// 2. D(const D&)
// 3. D(D&&)
// 4. D(int, int) <- 继承的
// 5. D(int) <- 继承的
};
二、using在类定义中
using 声明可以将基类成员引入到派生类的定义中,例如将基类的受保护成员暴露为派生类的公开成员。此时嵌套名说明符必须指名所定义的类的某个基类。如果这个名字是该基类的某个重载的成员函数的名字,那么具有该名字的所有基类成员函数均被引入。
class A_US
{
public:
A_US(int i){std::cout << "A_US(int)\n";}
A_US(double d, int i){std::cout << "A_US(double,int)\n";}
A_US(float f, int i, const char* c){std::cout << "A_US(float,int,char*)\n";}
};
class B_US:A_US
{
public:
virtual void Extralnterface(){} //扩展接口
};
class C_US:A_US
{
public:
//接口透传,实现父类构造函数集
C_US(int i):A_US(i){std::cout << "C_US(int)\n";}
C_US(double d, int i):A_US(d, i){std::cout << "C_US(double,int)\n";}
C_US(float f, int i, const char* c): A_US(f, i, c){std::cout << "C_US(float,int,char*)\n";}// ...
virtual void Extralnterface(){} //扩展接口
};
class D_US:A_US
{
public:
using A_US::A_US;
virtual void Extralnterface(){} //扩展接口
};
void using_test(void)
{
// B_US bu1(10); //无匹配构造函数
// B_US bu2(1.0,1); //无匹配构造函数
// B_US bu3(1.0,1,nullptr);//无匹配构造函数
C_US cu1(10);
C_US cu2(1.0,1);
C_US cu3(1.0,1,nullptr);
D_US du1(10);
D_US du2(1.0,1);
D_US du3(1.0,1,nullptr);
}
//out log
A_US(int)
C_US(int)
A_US(double,int)
C_US(double,int)
A_US(float,int,char*)
C_US(float,int,char*)
A_US(int)
A_US(double,int)
A_US(float,int,char*)
在上述代码中,派生类型只是想扩展一个接口,其他地方保持不变,B_US类型没有重新定义各种构造函数,实现不了各种对象创建方法;C_US费心费力定义了和基类A_US一致的构造函数,但是做了很多重复性工作;而类型D_US通过using引入基类的构造函数,直接省事省力和C_US一样的实现了基类构造。
三、 using引起的函数覆盖或隐藏
如果派生类已包含具有相同名字、形参列表和限定的成员,那么派生类成员隐藏或覆盖从基类引入的成员(不与之冲突)。
class A {
public:
virtual void f(int) { std::cout << "A::f\n"; }
void g(char) { std::cout << "A::g\n"; }
void h(int) { std::cout << "A::h\n"; }
protected:
int m; // A::m 是受保护的
typedef int value_type;
};
class B : public A {
public:
using A::m; // B::m 是公开的
using A::value_type; // B::value_type 是公开的
using A::f;
void f(int) { std::cout << "B::f\n"; } // B::f(int) 覆盖 A::f(int)
using A::g;
void g(int) { std::cout << "B::g\n"; } // g(int) 与 g(char) 均作为 B 成员可见
using A::h;
void h(int) { std::cout << "B::h\n"; } // B::h(int) 隐藏 A::h(int)
};
void using_test(void)
{
B b;
A& a = b;
// b.m = 2;// 错误,A::m 受保护
b.m = 1; // 受保护的 A::m 可作为公开的 B::m 访问
a.f(1); // 调用派生类 f()
b.f(1); // 调用派生类 f()
b.g(1); // 调用派生类 g(int)
b.g('a'); // 调用基类 g(char)
a.h(1); // 调用基类 h()
b.h(1); // 调用派生类 h()
}
//out log
B::f
B::f
B::g
A::g
A::h
B::h
上述代码中在派生类的函数g和基类的函数g是形参类型不同的,因此不会被覆盖,支持到两个g函数版本。而对于f函数,派生类会覆盖基类的虚函数,对于h函数,派生类会隐藏基类的普通函数。
四、using 引入基类构造函数的风险
如果 using 声明指代了正在定义的类的某个直接基类的构造函数(using Base::Base;),那么在初始化派生类时,该基类的所有构造函数(忽略成员访问)均对重载决议可见。如果重载决议选择了继承的构造函数,那么如果它被用于构造相应基类的对象时可访问,它也是可访问的:引入它的 using 声明的可访问性被忽略。
如果在初始化这种派生类对象时重载决议选择了继承的构造函数之一,那么用这个继承的构造函数对从之继承该构造函数的 Base 子对象进行初始化,而 Derived 的所有其他基类和成员,都如同以预置的默认构造函数一样进行初始化(如果提供默认成员初始化器则使用,否则进行默认初始化)。整个初始化被视作单个函数调用:继承的构造函数的各形参的初始化,按顺序早于派生类对象的任何基类或成员的初始化。
class A_US
{
public:
A_US(int i){std::cout << "A_US(int)\n";}
A_US(double d, int i){std::cout << "A_US(double,int)\n";}
A_US(float f, int i, const char* c){std::cout << "A_US(float,int,char*)\n";}
};
class E_US:A_US
{
public:
using A_US::A_US;
private:
int ival;
double dval;
};
//
void using_test(void)
{
// E_US eu; //错误没有默认构造
E_US eu1(10); //OK,调用A_US的,ival、dval被默认初始化(不进行初始化)
E_US eu2(1.0,1); //OK,调用A_US的,ival、dval被默认初始化(不进行初始化)
E_US eu3(1.0,1,nullptr);//OK,调用A_US的,ival、dval被默认初始化(不进行初始化)
}
如果派生类中的成员变量没有提供默认初始化,采用using引入基类的构造函数可能会导致错误。
class A_US
{
public:
A_US(int i){std::cout << "A_US(int)\n";}
A_US(double d, int i){std::cout << "A_US(double,int)\n";}
A_US(float f, int i, const char* c){std::cout << "A_US(float,int,char*)\n";}
};
class USTest
{
public:
USTest(int){};
};
class F_US:A_US
{
public:
using A_US::A_US;
private:
USTest ut;
};
//
// F_US fu1(10); //error,USTest无默认初始化导致错误
// F_US fu2(1.0,1); //error,USTest无默认初始化导致错误
// F_US fu3(1.0,1,nullptr);//error,USTest无默认初始化导致错误
这是因为通过using引入继承了基类的构造函数,只是对基类成员变量进行了初始化,为对于派生类本身的成员变量没有处理,只能寄望于成员变量本身提供默认初始化能力,否则就会出错。
五、using在多继承下的冲突问题
如果构造函数多个基类子对象继承,那么程序非良构,这与多继承的非静态成员函数相似。通过using引入基类的成员函数,需要注意多继承下的冲突问题:
class A1
{
public:
A1(int){};
};
class A2
{
public:
A2(int){};
};
//展示一
class B12 : public A1,A2
{
public:
using A1::A1;
using A2::A2; //冲突
};
B12 b12(10); //error
//展示二
class B12 : public A1,A2
{
public:
using A1::A1;
//using A2::A2;
};
B12 b12(10); //采用A1构造
//展示三
class B12 : public A1, public A2
{
public:
using A1::A1;
using A2::A2;
B12(int){}; //error: no matching function for call to 'A1::A1' and 'A2::A2()'
};
B12 b12(10); //
//展现四
class A1
{
public:
A1(){}; //支持默认构造
A1(int){};
};
class A2
{
public:
A2(){}; //支持默认构造
A2(int){};
};
//与任何其他非静态成员函数的 using 声明相同,如果继承的构造函数的签名与 Derived 的构造函数之一匹配,那么它被 Derived 中找到的版本从查找中隐藏。
//如果 Base 的继承构造函数恰好有与 Derived 的复制/移动构造函数匹配的签名,那么它不妨碍 Derived 复制/移动构造函数的隐式生成(然后继承的版本被其隐藏,这与 using operator= 类似)。
class B12 : public A1, public A2
{
public:
using A1::A1;//不生效,基类初始调用默认构造
using A2::A2;//不生效,基类初始调用默认构造
B12(int){};
};
B12 b12(10); //OK,采用B12构造
通过虚继承、显式禁用、访问授权等多种组合形式防止冲突发生。
class W_US { public: W_US(int){}; };
class X_US : virtual public W_US {
public:
using W_US::W_US; // 继承 W_US(int)
X_US() = delete; //默认构造强制删除
};
class Y_US : public X_US {
public:
using X_US::X_US;
};
class K_US : public W_US {
public:
K_US():W_US(0){}; //默认构造
private:
using W_US::W_US; // 继承 W_US(int),隐藏
};
class Z_US : public Y_US, public K_US, virtual public W_US {
public:
using Y_US::Y_US;
};
//
Z_US zus(0); // OK:Y_US 的初始化不调用 X_US 的默认构造函数
虚继承对于using引入基类函数引起的冲突规避同样有效:
class AU { public:AU(int){}; };
class BU : public AU { public:using AU::AU; };
class CU1 : public BU { public:using BU::BU; };
class CU2 : public BU { public:using BU::BU; };
class DU1 : public CU1, CU2 {
public:
using CU1::CU1;
using CU2::CU2;
};
class VU1 : virtual BU { public:using BU::BU; };
class VU2 : virtual BU { public:using BU::BU; };
class DU2 : public VU1, VU2 {
public:
using VU1::VU1;
using VU2::VU2;
};
//
// DU1 d1(0); // error,非良构:从不同的 B 基类子对象继承的构造函数
/*
*OK:只有一个 B 子对象。
*这初始化虚 B 基类,它初始化 A 基类
*然后如同用预置的默认构造函数
*初始化 V1 与 V2 基类*/
DU2 d2(0); //
六、using 变更访问权限
using 声明可以将基类成员引入到派生类的定义中,例如将基类的受保护成员暴露为派生类的公开成员。
class A3
{
protected: //保护函数
void f(int){std::cout <<"A3::f(int)\n";};
int ival;
};
class B3 : public A3
{
public:
using A3::f; //转换为公有函数
using A3::ival;
private:
};
class C3 : private B3 //私有继承
{
public:
using B3::f; //公有函数
private:
using B3::ival; //转为私有
};
//
B3 b3;
b3.ival = 10;//
b3.f(10); //
C3 c3;
//c3.ival = 10;//error
c3.f(10); //可外部调用
七、using 声明与模板参数
在模板化类中,如果 using 声明指代待决名(模板参数),而嵌套名说明符拥有与该无限定标识 相同的终止名,那么认为它指名构造函数。当采用实参传递时,模板参数类型明确,这是在创建该特例实现时,就会触发冲突问题。
using 声明不能指代命名空间,有作用域枚举项 (C++20 前),基类的析构函数,或用户定义转换函数的成员模板的特化,也不能using 声明指名成员模板的特化(语法不容许模板标识)。
class USTest
{
public:
USTest(int){std::cout<<"USTest(int)\n";};
};
template<class T>
class AT : public T {
public:
using T::T; // OK :继承 T 的构造函数
template<typename U> void f(U){};
private:
char cval;
};
template<class T, class U>
class BT : public T, AT<U> {
public:
using typename AT<U>::AT; // OK :继承 AT<U> 的构造函数
// using typename AT<int>::AT; // error,不能引入特例化
using typename T::T; // 继承 T 的构造函数
using typename AT<U>::f; //
using typename AT<T>::f; //
// using typename AT<int>::f; //error,不能引入特例化
void g() {
// f<int>(0); // 错误:已知 f 不是目标名,
// 因此 < 不开始模板实参列表
f(0); // OK
}
private:
int ival;
};
template<class T>
class CT : public T, AT<T> {
public:
using AT<T>::AT; // OK :继承 AT<T> 的构造函数,而AT<T> 继承T的构造函数
// using typename AT<int>::AT; // error :不能引入特例化
using T::T; // 同样继承 T 的构造函数
private:
int ival;
};
//
AT<int> at(); //warning: empty parentheses were disambiguated as a function declaration [-Wvexing-parse]
BT<int,float> bt(); //warning: empty parentheses were disambiguated as a function declaration [-Wvexing-parse]
CT<int> ct(); //warning: empty parentheses were disambiguated as a function declaration [-Wvexing-parse]
//
AT<USTest> at1(); //warning: empty parentheses were disambiguated as a function declaration [-Wvexing-parse]
BT<USTest,float> bt1(); //warning: empty parentheses were disambiguated as a function declaration [-Wvexing-parse]
CT<USTest> ct1(); //warning: empty parentheses were disambiguated as a function declaration [-Wvexing-parse]
//
AT<USTest> at2(1); //OK
// BT<USTest,USTest> bt2(1); //error,多个构造冲突
// BT<USTest,float> bt2(1);//error,base type 'float' fails to be a struct or class type float(int)
// CT<USTest> ct2(1); //error,多个构造冲突
八、using 声明与枚举项
C++20 起,using 声明也能将枚举的枚举项引入命名空间、块和类作用域。using 声明也能用于无作用域枚举项。using 声明枚举类型名时,并不传送其枚举项。
enum num { aval,bval };
using num::aval; // OK ,c++11
//以下c++20
enum class direction { east, west,north,south};
struct walk {
using direction::east;
direction e = east; // OK
// direction n = north;// error,north并没有被using,使用时需要+作用域
direction n = direction::north;
};
using direction::north;
constexpr direction norw = north; // OK
constexpr auto get_dir(bool is_nor)
{
using direction::north, direction::south;
return is_nor ? north : south; // OK
}
九、using 声明与操作符问题
如果 using 声明引入基类的赋值运算符到派生类,而其签名恰好与派生类的复制赋值或移动赋值运算符匹配,那么该运算符被派生类的隐式声明的复制/移动赋值运算符隐藏。
class OP_US
{
protected:
/* data */
int ival;
public:
OP_US& operator=(const OP_US& obj){
std::cout << "OP_US operator=&\n";
if(this==&obj) return *this;
ival = obj.ival;
return *this;
};
};
class DOP_US1 : public OP_US
{
private:
/* data */
public:
using OP_US::operator=;
};
class DOP_US2 : public OP_US
{
private:
/* data */
public:
using OP_US::operator=;
DOP_US2& operator=(const DOP_US2& obj){
std::cout << "DOP_US2 operator=&\n";
if(this==&obj) return *this;
ival = obj.ival;
return *this;
};
};
class DOP_US3 : public OP_US
{
private:
/* data */
double dval;
public:
using OP_US::operator=;
DOP_US3& operator=(const DOP_US3&& obj){
std::cout << "DOP_US3 operator= &&\n";
ival = std::move(obj.ival);
return *this;
};
};
//
DOP_US1 dop1,dop2;
dop2 = dop1; //OP_US operator=&
DOP_US2 dop3,dop4;
dop4 = dop3; //OP_US2 operator=&
DOP_US3 dop5,dop6;
// dop6 = dop5; //error: use of deleted function 'DOP_US3& DOP_US3::operator=(const DOP_US3&)'
dop6 = std::move(dop5);//OP_US3 operator=&&
相同的规则适用于继承恰好与派生类的移动/复制构造函数匹配的基类构造函数的 using 声明(C++11 起)。
感谢读友能耐心看到最后,本人文章都很长,知识点很细,可能会有所遗漏和失误,如果有不妥之处,烦请指出。如果内容对您有所触动,请点赞关注一下防止不迷路。
十、源码补充
编译指令g++ main.cpp test*.cpp -o test.exe -std=c++11(或c++20).
main.cpp
#include "test1.h"
int main(int argc, char* argv[])
{
using_test();
return 0;
}
test1.h
#ifndef _TEST_1_H_
#define _TEST_1_H_
void using_test(void);
#endif //_TEST_1_H_
test1.cpp
#include "test1.h"
#include <iostream>
#include <stdexcept>
class A {
public:
virtual void f(int) { std::cout << "A::f\n"; }
void g(char) { std::cout << "A::g\n"; }
void h(int) { std::cout << "A::h\n"; }
protected:
int m; // A::m 是受保护的
typedef int value_type;
};
class B : public A {
public:
using A::m; // B::m 是公开的
using A::value_type; // B::value_type 是公开的
using A::f;
void f(int) { std::cout << "B::f\n"; } // B::f(int) 覆盖 A::f(int)
using A::g;
void g(int) { std::cout << "B::g\n"; } // g(int) 与 g(char) 均作为 B 成员可见
using A::h;
void h(int) { std::cout << "B::h\n"; } // B::h(int) 隐藏 A::h(int)
};
class A_US
{
public:
A_US(int i){std::cout << "A_US(int)\n";}
A_US(double d, int i){std::cout << "A_US(double,int)\n";}
A_US(float f, int i, const char* c){std::cout << "A_US(float,int,char*)\n";}
};
class B_US:A_US
{
public:
virtual void Extralnterface(){} //扩展接口
};
class C_US:A_US
{
public:
//接口透传,实现父类构造函数集
C_US(int i):A_US(i){std::cout << "C_US(int)\n";}
C_US(double d, int i):A_US(d, i){std::cout << "C_US(double,int)\n";}
C_US(float f, int i, const char* c): A_US(f, i, c){std::cout << "C_US(float,int,char*)\n";}// ...
virtual void Extralnterface(){} //扩展接口
};
class D_US:A_US
{
public:
using A_US::A_US;
virtual void Extralnterface(){} //扩展接口
};
class E_US:A_US
{
public:
using A_US::A_US;
private:
int ival;
double dval;
};
class USTest
{
public:
USTest(int){std::cout<<"USTest(int)\n";};
};
class F_US:A_US
{
public:
using A_US::A_US;
private:
USTest ut;
};
class A1
{
public:
A1(){};
A1(int){};
};
class A2
{
public:
A2(){};
A2(int){};
};
class B12 : public A1, public A2
{
public:
using A1::A1;
using A2::A2;
B12(int){};
};
class W_US { public: W_US(int){}; };
class X_US : virtual public W_US {
public:
using W_US::W_US; // 继承 W_US(int)
X_US() = delete;
};
class Y_US : public X_US {
public:
using X_US::X_US;
};
class K_US : public W_US {
public:
K_US():W_US(0){};
private:
using W_US::W_US; // 继承 W_US(int),隐藏
};
class Z_US : public Y_US, public K_US, virtual public W_US {
public:
using Y_US::Y_US;
};
class AU { public:AU(int){}; };
class BU : public AU { public:using AU::AU; };
class CU1 : public BU { public:using BU::BU; };
class CU2 : public BU { public:using BU::BU; };
class DU1 : public CU1, CU2 {
public:
using CU1::CU1;
using CU2::CU2;
};
class VU1 : virtual BU { public:using BU::BU; };
class VU2 : virtual BU { public:using BU::BU; };
class DU2 : public VU1, VU2 {
public:
using VU1::VU1;
using VU2::VU2;
};
class A3
{
protected: //保护函数
void f(int){std::cout <<"A3::f(int)\n";};
int ival;
};
class B3 : public A3
{
public:
using A3::f; //转换为公有函数
using A3::ival;
private:
};
class C3 : private B3 //私有继承
{
public:
using B3::f; //公有函数
private:
using B3::ival; //转为私有
};
template<class T>
class AT : public T {
public:
using T::T; // OK :继承 T 的构造函数
template<typename U> void f(U){};
private:
char cval;
};
template<class T, class U>
class BT : public T, AT<U> {
public:
using typename AT<U>::AT; // OK :继承 AT<U> 的构造函数
// using typename AT<int>::AT; // error,不能引入特例化
using typename T::T; // 继承 T 的构造函数
using typename AT<U>::f; //能编译,但 f 不是模板名
using typename AT<T>::f; //能编译,但 f 不是模板名
// using typename AT<int>::f; //error,不能引入特例化
void g() {
// f<int>(0); // 错误:已知 f 不是目标名,
// 因此 < 不开始模板实参列表
f(0); // OK
}
private:
int ival;
};
template<class T>
class CT : public T, AT<T> {
public:
using typename AT<T>::AT; // OK :继承 AT<U> 的构造函数
// using typename AT<int>::AT; // error :不能引入特例化
using typename T::T; // 继承 T 的构造函数
private:
int ival;
};
enum num { aval,bval };
using num::aval; // OK ,c++11
#if __cplusplus > 201103L
//以下c++20
enum class direction { east, west,north,south};
struct walk {
using direction::east;
direction e = east; // OK
// direction n = north;// error,north并没有被using,使用时需要+作用域
direction n = direction::north;
};
using direction::north;
constexpr direction norw = north; // OK
constexpr auto get_dir(bool is_nor)
{
using direction::north, direction::south;
return is_nor ? north : south; // OK
}
#endif
class OP_US
{
protected:
/* data */
int ival;
public:
OP_US& operator=(const OP_US& obj){
std::cout << "OP_US operator= &\n";
if(this==&obj) return *this;
ival = obj.ival;
return *this;
};
};
class DOP_US1 : public OP_US
{
private:
/* data */
public:
using OP_US::operator=;
};
class DOP_US2 : public OP_US
{
private:
/* data */
public:
using OP_US::operator=;
DOP_US2& operator=(const DOP_US2& obj){
std::cout << "DOP_US2 operator= &\n";
if(this==&obj) return *this;
ival = obj.ival;
return *this;
};
};
class DOP_US3 : public OP_US
{
private:
/* data */
double dval;
public:
using OP_US::operator=;
DOP_US3& operator=(const DOP_US3&& obj){
std::cout << "DOP_US3 operator= &&\n";
ival = std::move(obj.ival);
return *this;
};
};
void using_test(void)
{
B b;
A& a = b;
// b.m = 2;// 错误,A::m 受保护
b.m = 1; // 受保护的 A::m 可作为公开的 B::m 访问
a.f(1); // 调用派生类 f()
b.f(1); // 调用派生类 f()
b.g(1); // 调用派生类 g(int)
b.g('a'); // 调用基类 g(char)
a.h(1); // 调用基类 h()
b.h(1); // 调用派生类 h()
//
// B_US bu1(10); //无匹配构造函数
// B_US bu2(1.0,1); //无匹配构造函数
// B_US bu3(1.0,1,nullptr);//无匹配构造函数
C_US cu1(10);
C_US cu2(1.0,1);
C_US cu3(1.0,1,nullptr);
D_US du1(10);
D_US du2(1.0,1);
D_US du3(1.0,1,nullptr);
//
// E_US eu; //错误没有默认构造
E_US eu1(10); //OK,调用A_US的,ival、dval被默认初始化(不进行初始化)
E_US eu2(1.0,1); //OK,调用A_US的,ival、dval被默认初始化(不进行初始化)
E_US eu3(1.0,1,nullptr);//OK,调用A_US的,ival、dval被默认初始化(不进行初始化)
//
// F_US fu1(10); //error,USTest无默认初始化导致错误
// F_US fu2(1.0,1); //error,USTest无默认初始化导致错误
// F_US fu3(1.0,1,nullptr);//error,USTest无默认初始化导致错误
//
B12 b12(10); //采用B12构造
Z_US zus(0); // OK:Y_US 的初始化不调用 X_US 的默认构造函数
//
// DU1 d1(0); // error,非良构:从不同的 BU 基类子对象继承的构造函数
/*
*OK:只有一个 BU 子对象。
*这初始化虚 BU 基类,它初始化 AU 基类
*然后如同用预置的默认构造函数
*初始化 VU1 与 VU2 基类*/
DU2 d2(0); //
//
B3 b3;
b3.ival = 10;//
b3.f(10); //
C3 c3;
c3.f(10); //可外部调用
std::cout <<"-----1-----\n";
//
AT<int> at(); //warning: empty parentheses were disambiguated as a function declaration [-Wvexing-parse]
BT<int,float> bt(); //warning: empty parentheses were disambiguated as a function declaration [-Wvexing-parse]
CT<int> ct(); //warning: empty parentheses were disambiguated as a function declaration [-Wvexing-parse]
//
AT<USTest> at1(); //warning: empty parentheses were disambiguated as a function declaration [-Wvexing-parse]
BT<USTest,float> bt1(); //warning: empty parentheses were disambiguated as a function declaration [-Wvexing-parse]
CT<USTest> ct1(); //warning: empty parentheses were disambiguated as a function declaration [-Wvexing-parse]
//
AT<USTest> at2(1); //OK
// BT<USTest,USTest> bt2(1); //error,多个构造冲突
// BT<USTest,float> bt2(1);//error,base type 'float' fails to be a struct or class type float(int)
// CT<USTest> ct2(1); //error,多个构造冲突
//
DOP_US1 dop1,dop2;
dop2 = dop1; //OP_US operator=&
DOP_US2 dop3,dop4;
dop4 = dop3; //OP_US2 operator=&
DOP_US3 dop5,dop6;
// dop6 = dop5; //error: use of deleted function 'DOP_US3& DOP_US3::operator=(const DOP_US3&)'
dop6 = std::move(dop5);//OP_US3 operator=&&
}