在派生类中使用基类的模板typedef/using

8

在使用一个有模板的基类中的using时,我遇到了冗长的问题。在下面的代码中,派生类试图使用其基类中的my_type

template <typename T>
class Base {
    public:
        using mytype = T;
};

template <typename T>
class Derived : public Base<T>{
    public:
        // using the base's mytype here
        static typename Base<T>::mytype func() { return 0;}
};

然而,在实践中,我发现这对于看起来应该更简单的事情来说是非常繁琐的。如果基类不是模板化的,则不需要<T>或typename(显然)。
在我的实际问题中,我有大量从基类派生的类,并且我希望尽可能简化此操作。我目前所拥有的内容就像下一个示例一样,我只是添加了一个额外的using以从基类获取类型,但它感觉像是我不需要拥有的多余层。
template <typename T>
class Derived : public Base<T>{
    public:
        using base_type = typename Base<T>::mytype;
        static base_type func() { return 0;}
};

这可能看起来像是一个愚蠢的问题,但在派生类中使用基类的mytype的次数非常多,使得前者相当丑陋。有没有一种正确的方法可以从模板化的基类中获取类型并保持可读性?


5
那里没有你真正可以做的事情。语言就是它本来的样子,这是查找操作要求的一部分。 - David Rodríguez - dribeas
我同意@DavidRodríguez-dribeas的观点。你使用usingtypedef的解决方案也是我会使用的。 - user955279
当然,你可以稍微缩短它,比如说 using base = Base<T>; static typename base::mytype func(); 或者 using self = Derived; static typename self::mytype func();. - dyp
1
你在基类中将 mytype 设置为 T,并在派生类中将 T 作为模板参数传递。为什么不直接将 T 作为函数的返回类型呢? - Zac Howland
@ZacHowland 这只是一个简化的例子。在真实版本中涉及到了很多 decltype - Ryan Haining
2个回答

16
这是语言的一个众所周知的怪癖,没有真正的解决方案。在模板中进行查找分为两个步骤,在实例化之前的第一阶段中,非依赖名称被解析为它们的含义,而在第二阶段中,依赖名称在实例化之后被解析。
将两个阶段分开是为了为模板开发人员提供一些理智,他们不知道模板将在哪里实例化。查找的第一阶段在模板定义的时候完成,并且可以在开发人员确切的那一点上推理。在某个时候,模板将执行依赖于参数的操作,而这些操作不能在模板定义的地方解决,因为模板参数尚未固定。这些名称被视为依赖项,并且查找被推迟到第二阶段,在替换模板参数之后进行,以便ADL可以启动。
这与您特定的问题有什么关系?当您从非模板基类继承时,开发人员已经确定了基类是什么,并且可以在模板定义的点上查找。但是当基类依赖于模板参数时,派生模板定义的位置不知道基类的定义。特别是,在替换类型之前,编译器不可能知道是否存在此特定类型的专门化。这意味着在第一阶段期间,编译器不能假设基类的任何内容,因此查找无法搜索其中。
使用typedef作为基类的直接方法是,派生模板的开发人员明确告诉编译器它需要一个类型,并且该类型将在基类模板的实例化中定义。关键点在于编译器对基类一无所知,但是开发人员可以要求使用此模板的合同满足一个约定,即基类的实例化必须具有该嵌套类型。开发人员可以自由添加其模板类型的约束条件,而编译器不行。
用于这个的语法是您第一个块中的语法:typename Base<T>::type来引用该类型,这告诉编译器使用契约要求,无论用于实例化DerivedT是什么,用户都必须确保Base<T>将包含一个嵌套成员type,该成员是一种类型(typename)。作为简写,您可以选择第二种方法:创建一个本地typedef,该typedef将在Derived中找到并解析为那个。在这种情况下,在第一阶段的常规查找将找到嵌套的typedef,确定它是指依赖名称,并将完整查找推迟到第二阶段。

虽然这并不是回答是否有更好的方式(从可读性方面来看),但我希望它能让你理解为什么事情是这样的。在设计语言时做出的决定并不是随意的[可能不是理想的,有些人认为第一阶段是不必要和不受欢迎的,但它不是随意的]


6
也许我错过了一些显而易见的东西,但是您可以直接在不必给它一个新名称的情况下,对该类型使用 using
template <typename T>
class Derived : public Base<T>{
public:
    using typename Base<T>::mytype;
    static mytype func() { return 0;}
};

你甚至可以决定mytypeusing声明应该放在publicprotectedprivate部分。


2
只要基类中没有太多的 mytype,或者你对使用声明感到舒适,这样做是可以的。我认为OP寻找一种直接的方式,避免在派生类中使用任何别名或使用声明。 - dyp

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接