为什么在使用可变构造函数时必须创建类型别名
Why must I create a type alias when using a variadic constructor function?
我有一个模板化基类,它采用N种类型:
template <typename... Ts>
class Base{};
在该基类上使用受保护的继承时,
template <typename... Ts>
class Derived : protected Base<Ts...>{
//like so...
};
我想另外包括基类的公共构造函数:
template <typename... Ts>
class Derived : protected Base<Ts...>{
//create an alias
using Parent = Base<Ts...>;
//get all constructors as well
using Parent::Parent;
};
这是有效的
但是,为什么必须包含Parent
别名?
似乎没有它我就无法获得构造函数。以下尝试不起作用:
template <typename... Ts>
class Derived : protected Base<Ts...>{
//get all constructors as well
using Base<Ts...>::Base<Ts...>;
};
错误:
clang++ -std=c++1z -o main v.cpp
error: expected ';' after using declaration
using Base<Ts...>::Base<Ts...>;
^
;
1 error generated.
我可以剪掉模板部分,然后编译,但这似乎不正确:
template <typename... Ts>
class Derived : protected Base<Ts...>{
//get all constructors as well
using Base<Ts...>::Base;
};
我认为它不正确的原因是它似乎对向量不起作用
不编译:
template <typename... Ts>
class Derived : protected std::vector<Ts...>{
//get all constructors as well
using std::vector<Ts...>::std::vector;
};
但是,使用别名确实有效
编译:
template <typename... Ts>
class Derived : protected std::vector<Ts...>{
//create an alias
using Parent = std::vector<Ts...>;
//get all constructors as well
using Parent::Parent;
};
问题:
我是否必须使用别名才能获得相同的功能,或者有没有一种方法可以在不为基类型创建新名称的情况下内联它?
您不需要类型别名。问题是Base<Ts...>
的构造函数不被称为Base<Ts...>
。它被称为Base
。
这项工作:
template<class...Ts>
struct Base {
};
template<class...Ts>
struct Derived : Base<Ts...>
{
using Base<Ts...>::Base;
};
此外,std::vector<...>
的构造函数的名称不是std::vector
,而是vector
这也起作用:
template<class...Ts>
struct Derived : std::vector<Ts...>
{
using std::vector<Ts...>::vector;
};
但是,不是从std::containers派生的封装它们。
在您的示例中,Base
没有模板构造函数,因此using Base<Ts...>::Base<Ts...>;
试图找到一个不存在的构造函数。
想象一下,我有一门课,就像你的
class Base{
public:
Base(){}
template<typename ... Ts>
Base(){}
};
using Base<Ts...>::Base<Ts...>
会选择哪个构造函数?
using Parent = Base<Ts...>
之所以有效,是因为在编写using Parent::Parent
时,您试图找到一个而非模板化的Parent
构造函数。它扩展到using Base<Ts...>::Base;
。
using Base<Ts...>::Base<Ts...>;
是非法的,因为在[namespace.udcl]p5:中有一条非常通用的规则
使用声明的不应命名模板id。
这与在使用声明时无法命名特定重载的情况非常相似。不过,我不知道这项规则的理据为何。
构造函数和名称之间的关系。。。很复杂:
- "构造函数没有名称。"[class.ctor]p1
- "使用不命名构造函数的声明的的声明点[…]"[class.ctor]p4
因此,虽然构造函数没有的名称,但它们可以命名为。对我来说,这似乎类似于匿名类型:
struct { int m; } x;
decltype(x) // refers to the type of `x` which has no name
应继承构造函数的using声明必须命名构造函数。
[class.qual]p2指定如何在限定id中命名构造函数(由嵌套名称说明符和非限定id组成):
在函数名不被忽略并且嵌套名称说明符命名类
C
:
- (2.1)如果在
C
中查找时,在嵌套名称说明符之后指定的名称是C
的注入类名,或者- (2.2)在作为成员声明的using声明中,如果在嵌套名称说明符之后指定的名称与标识符或简单模板id的模板名称嵌套名称说明符的组件
该名称被认为是对类CCD_ 19的构造函数进行命名。
为了解决/评论其他一些答案,我会跑题一点。。
可以说(2.1)不适用于OP:虽然Base
包含一个注入的类名,但这个名称是Base
,而不是Base<Ts...>
。在嵌套名称说明符之后指定的名称是Base<Ts...>
,它不是注入的类名。
注意,依赖基类的注入类名直到实例化时才知道。考虑
template<typename T>
struct foo : T {
using T::name;
void name(double);
};
struct name { name(int); };
struct bar { void name(int); };
foo<name> // constructor inheritance?
foo<bar> // makes a function visible?
clang++拒绝这个例子,而g++接受它。这是CWG2070
然而,在定义时,我们确实知道OP中的Base
是一个类,因此可以推断注入的类名。这需要区分在定义时名称已知的类型和不知道名称的类型。别名模板可以隐藏这一点,因此这不是using声明中限定id的形式的一个微不足道的区别。
在任何情况下,项目符号(2.2)都不适用于Base<Ts...>::Base<Ts...>
:其嵌套名称说明符为Base<Ts...>
。此模板的模板名称为Base
。嵌套名称说明符之后的名称为Base<Ts...>
。
[class.qual]p2.2允许使用Base<Ts...>::Base
和parent::parent
,只需查看限定id中使用的标识符即可。此处不需要执行实际的名称查找。表单本身足以命名构造函数。
- 在类 A 中创建类型为 B 类的向量 - 访问数据 [C++] [成员在两个类中都是私有的]
- 如何在C++中创建类型列表的 n 路笛卡尔积?
- 使用 C++17,如何创建类型到值的编译时图?
- C++ 中的对象创建类型有什么区别?
- 在 C++ 中创建类型向量
- 创建类型列表并访问每种类型的静态成员?
- 无法创建类型为无序链接列表的对象
- 如何为指针创建类型定义
- 在没有宏的情况下在 C++98 中创建类型列表时遇到问题
- 使用特定成员变量值创建类型的类型
- <T> 从类型对象创建类型<T1>对象的构造函数
- 创建类型结构的指针阵列
- C++当类型T需要构造函数时,是否可以创建类型T的std::列表
- 在C++中创建类型定义
- 是否可以在 c++ 中创建类型的向量
- 创建类型为 'this'/当前对象的指针
- 如果模板化"ResourceCache"需要不同的创建参数/方法,它们如何加载/创建 T 类型的资源?
- ptr_fun - 无法创建类型?
- 如何从继承的自定义类 c++ 创建类型
- DLL 创建类型