为什么在使用可变构造函数时必须创建类型别名?
Why must I create a type alias when using a variadic constructor function?
我有一个模板化的基础 class,它需要 N 种类型:
template <typename... Ts>
class Base{};
在该基础上使用受保护的继承时 class,
template <typename... Ts>
class Derived : protected Base<Ts...>{
//like so...
};
我想另外包含基础 class 的 public 构造函数:
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
没有模板构造函数,因此 using Base<Ts...>::Base<Ts...>;
正在尝试查找不存在的构造函数。
假设我有一个 class 喜欢 你的
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;
.
您不需要类型别名。问题是 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:: 容器派生! 封装它们。
using Base<Ts...>::Base<Ts...>;
是非法的,因为 [namespace.udecl]p5:
中有一条非常普遍的规则
A using-declaration shall not name a template-id.
这与您不能在 using 声明中命名特定重载的事实非常相似。但是,我不知道这条规则的基本原理。
构造函数和名称之间的关系...很复杂:
- "Constructors do not have names." [class.ctor]p1
- "The point of declaration of a using-declaration that does not name a constructor [...]" [class.ctor]p4
因此,尽管构造函数没有名称,但它们可以被命名。对我来说,这似乎类似于匿名类型:
struct { int m; } x;
decltype(x) // refers to the type of `x` which has no name
应继承构造函数的 using 声明 必须命名一个构造函数。
[class.qual]p2 指定如何在限定 ID 中命名构造函数(由 nested-name-specifier 和 不合格的 id):
In a lookup in which function names are not ignored and the
nested-name-specifier nominates a class C
:
- (2.1) if the name specified after the nested-name-specifier, when looked up in
C
, is the injected-class-name of C
, or
- (2.2) in a using-declaration that is a member-declaration, if the name specified after the nested-name-specifier is the same as
the identifier or the simple-template-id’s template-name in the last
component of the nested-name-specifier,
the name is instead considered to name the constructor of class C
.
对于address/comment一些其他的回答,我会跑题了..
可以争辩说 (2.1) 不适用于 OP:虽然 Base
包含一个 injected-class-name,这个名字是Base
而不是 Base<Ts...>
。在 nested-name-specifier 之后指定的名称是 Base<Ts...>
,这不是 injected-class-name .
请注意,依赖基 class 的 injected-class-name 在实例化之前是未知的。考虑
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++ 接受它。这是CWG 2070
我们确实知道,在定义点,OP 中的 Base
是 class,因此可以 推断 注入-class-名字。这将需要区分名称在定义时已知的类型和名称未知的类型。别名模板可以隐藏这一点,因此这不是 using 声明中合格 ID 的 形式 的微不足道的区别。
无论如何,要点 (2.2) 不适用于 Base<Ts...>::Base<Ts...>
:它的 nested-name-specifier 是 Base<Ts...>
。 模板名称 是 Base
。 nested-name-specifier 之后的名称是 Base<Ts...>
.
[class.qual]p2.2 允许使用 Base<Ts...>::Base
和 parent::parent
,只需 通过查看 qualified-id[=85= 中使用的标识符].此处不需要执行实际的名称查找。表单本身足以命名构造函数。
我有一个模板化的基础 class,它需要 N 种类型:
template <typename... Ts>
class Base{};
在该基础上使用受保护的继承时 class,
template <typename... Ts>
class Derived : protected Base<Ts...>{
//like so...
};
我想另外包含基础 class 的 public 构造函数:
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
没有模板构造函数,因此 using Base<Ts...>::Base<Ts...>;
正在尝试查找不存在的构造函数。
假设我有一个 class 喜欢 你的
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;
.
您不需要类型别名。问题是 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:: 容器派生! 封装它们。
using Base<Ts...>::Base<Ts...>;
是非法的,因为 [namespace.udecl]p5:
A using-declaration shall not name a template-id.
这与您不能在 using 声明中命名特定重载的事实非常相似。但是,我不知道这条规则的基本原理。
构造函数和名称之间的关系...很复杂:
- "Constructors do not have names." [class.ctor]p1
- "The point of declaration of a using-declaration that does not name a constructor [...]" [class.ctor]p4
因此,尽管构造函数没有名称,但它们可以被命名。对我来说,这似乎类似于匿名类型:
struct { int m; } x;
decltype(x) // refers to the type of `x` which has no name
应继承构造函数的 using 声明 必须命名一个构造函数。
[class.qual]p2 指定如何在限定 ID 中命名构造函数(由 nested-name-specifier 和 不合格的 id):
In a lookup in which function names are not ignored and the nested-name-specifier nominates a class
C
:
- (2.1) if the name specified after the nested-name-specifier, when looked up in
C
, is the injected-class-name ofC
, or- (2.2) in a using-declaration that is a member-declaration, if the name specified after the nested-name-specifier is the same as the identifier or the simple-template-id’s template-name in the last component of the nested-name-specifier,
the name is instead considered to name the constructor of class
C
.
对于address/comment一些其他的回答,我会跑题了..
可以争辩说 (2.1) 不适用于 OP:虽然 Base
包含一个 injected-class-name,这个名字是Base
而不是 Base<Ts...>
。在 nested-name-specifier 之后指定的名称是 Base<Ts...>
,这不是 injected-class-name .
请注意,依赖基 class 的 injected-class-name 在实例化之前是未知的。考虑
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++ 接受它。这是CWG 2070
我们确实知道,在定义点,OP 中的 Base
是 class,因此可以 推断 注入-class-名字。这将需要区分名称在定义时已知的类型和名称未知的类型。别名模板可以隐藏这一点,因此这不是 using 声明中合格 ID 的 形式 的微不足道的区别。
无论如何,要点 (2.2) 不适用于 Base<Ts...>::Base<Ts...>
:它的 nested-name-specifier 是 Base<Ts...>
。 模板名称 是 Base
。 nested-name-specifier 之后的名称是 Base<Ts...>
.
[class.qual]p2.2 允许使用 Base<Ts...>::Base
和 parent::parent
,只需 通过查看 qualified-id[=85= 中使用的标识符].此处不需要执行实际的名称查找。表单本身足以命名构造函数。