我有一个模板化的基类,需要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;
};
问题:
我是否必须使用别名才能在功能上获得相同的功能,或者有没有一种方法可以在不为基类型创建新名称的情况下内联它?
答案 0 :(得分:4)
您不需要类型别名。问题是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派生的!封装它们。
答案 1 :(得分:2)
在您的示例中,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;
。
答案 2 :(得分:1)
using Base<Ts...>::Base<Ts...>;
是非法的,因为[namespace.udecl] p5中的一般规则:
using-declaration 不得命名 template-id 。
这与使用声明中无法命名特定重载的事实非常类似。但是,我不知道这条规则的基本原理。
构造函数和名称之间的关系......很复杂:
因此,虽然构造函数没有名称,但它们可以命名为。对我来说,这似乎与匿名类型类似:
struct { int m; } x;
decltype(x) // refers to the type of `x` which has no name
继承构造函数的 using-declaration 必须命名构造函数。
[class.qual] p2指定如何在qualified-id中命名构造函数(由嵌套名称说明符和 unqualified-id 组成):
在查找中,不忽略函数名称和 nested-name-specifier 指定一个类
C
:
- (2.1)如果在
C
中查找嵌套名称说明符之后指定的名称是的注入类名称C
或- (2.2)在 using-declaration 中是 member-declaration ,如果在 nested-name-specifier 之后指定的名称是相同的 最后的标识符或 simple-template-id 的模板名称 嵌套名称说明符的组件,
而是将名称视为命名类
C
的构造函数。
为了解决/评论其他一些答案,我会稍微离开......
可以说(2.1)不适用于OP:虽然Base
包含注入类名,但此名称为Base
而不是{{ 1}}。 嵌套名称说明符之后指定的名称是Base<Ts...>
,而不是 inject-class-name 。
请注意,在实例化时间之前,不知道依赖基类的注入类名。考虑
Base<Ts...>
clang ++拒绝这个例子,而g ++接受它。这是CWG 2070
我们确实知道,在定义时,OP中的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?
是一个类,因此可以推断 注入类名。这需要区分在定义点知道名称的类型和不在定义点的名称。别名模板可以隐藏它,因此这不是使用声明中qualified-id的 form 的微不足道的区别。
在任何情况下,项目符号(2.2)都不适用于Base
:其嵌套名称说明符为Base<Ts...>::Base<Ts...>
。 模板名称是Base<Ts...>
。 嵌套名称说明符之后的名称是Base
。
[class.qual] p2.2允许使用Base<Ts...>
和Base<Ts...>::Base
,只需查看qualified-id 中使用的标识符即可。这里不需要执行实际的名称查找。表单本身足以命名构造函数。