【问题标题】:Two-phase lookup: is it possible to easily mix inheritence and templates两阶段查找:是否可以轻松混合继承和模板
【发布时间】:2014-03-12 18:26:28
【问题描述】:

简介: C++ 标准区分了依赖于模板参数的符号名称和不依赖模板参数的名称,这称为两阶段名称查找(参见here)。定义模板时会尽快解析非相关名称。另一方面,依赖名称仅在模板实例化时解析。

示例:

template<class T> struct Base {
    typedef T type;
    static const int n = 3;
    virtual int f() = 0;
    int f(int x) { return x * 2; }
};

// doesn't compile!
template<class T> struct Derived : Base<T> {
    type field;         // The compiler doesn't know Base<T>::type yet!
    int f() { return n; } // the compiler doesn't know n yet, and f(int) is maksed!
};

目前,我所做的是这样定义Derived

template<class T> struct Derived : Base<T> {
    typedef Base<T> Parent;
    typedef typename Parent::type type; // correct but
    using Parent::n;                    // boring, long
    using Parent::f;                    // and harder to maintain
    type field;
    int f() { return n; }
};

对我来说,面向对象编程的主要目标之一是减少代码重复;这种失败的目的……

问题:是否有另一种方法可以通过使用一些我不知道的语法或聪明的技巧来定义Derived?我喜欢这样的东西:

template<class T> struct Derived : Base<T> {
    using Base<T>::*; // I promise I won't do strange specializations of Base<T>
    type field;
    int f() { return n; }
};

编辑澄清:也许我不够具体。假设您在Base 中有大约十个类型定义/字段/函数,以及数十个派生类,每个类的特定代码不到 5 行。这意味着大部分代码将包含重复的 typedef 和 using 子句,我知道没有办法完全避免这种情况,但我希望尽量减少这种重复代码。

感谢您提供任何使这更易于编写和维护的想法!

【问题讨论】:

  • 字符串typename Base&lt;T&gt;::type T field;是什么意思?应该是typename Base&lt;T&gt;::type field; 或者T field;,我觉得……
  • @Constructor:谢谢,已修复!
  • 这个问题有4种解决方案:1)使用前缀Base&lt;T&gt;::n,2)使用前缀this-&gt;n,3) 添加语句using Base&lt;T&gt;::n, 4) 使用启用许可模式的全局编译器开关。这些解决方案的优缺点和细节在stackoverflow.com/questions/50321788/…

标签: c++ templates inheritance


【解决方案1】:
T field;

这应该不是问题; T 是模板参数本身,而不是依赖名称。

return n;

这确实是一个问题,因为它是一个从属名称并且不知道它是一个成员。最简单的解决方案是

return this->n;

Base&lt;T&gt;::nDerived::n 也可以,但我不想重复类名。

更新

type field;

不幸的是,没有比访问依赖类型名更简单的技巧了

typename Base<T>::type field;

【讨论】:

  • 谢谢,很抱歉我在示例代码中犯了错误!我的意思是一个依赖名称,例如 Base::type,我编辑了我的问题。该代码只是一个示例 - 假设您有几个依赖类型是否可以轻松使用它们?
  • +1 表示this-&gt; 表示字段。更微妙的 f 怎么样:如果没有 using 名称掩码,可能会发生不好的事情。
  • @Antoine:你的意思是如果你想专门调用基类f,而不是隐藏它的派生类的成员?您必须指定Base&lt;T&gt;::f;就像没有模板一样。
  • @Antoine f() 有什么问题?在您的示例中,您不需要using Parent::f; 声明,因为f() 是在Derived 中定义的。现在,假设Base::f() 不是纯虚拟的,并且您没有在Derived 中覆盖它,如果您想在Derived 的某个成员函数中调用它,请将其调用为this-&gt;f(); Example
  • @Example: 如果我不做using Parent::f 不会f 被定义为非虚函数吗?这可能会让我在运行时搞砸。此外,有时 f(int) 是否被屏蔽(在这种情况下不确定,但我知道我在没有 using 的情况下遇到了重载函数的问题)
【解决方案2】:

听我说完

#include <string>
#include <iostream>

template<class T> struct Base {
    typedef T type;
    static const int n = 3;
    virtual int f() = 0;
    int f(int x) { return x * 2; }
};

// does compile
template< class T, template<typename> class Base = Base > 
struct Derived : Base<T> 
{
    typename Base<T>::type field; 
    int f() 
    {
        field = 200;
        return n;
    }
    int f(int x)
    {
        return Base<T>::f(x);
    }
};

int main()
{
    Derived<int> bss;
    std::cout << bss.f() << std::endl;
    std::cout << bss.f(50) << std::endl;
    std::cout << bss.field << std::endl;

    return 0;
}

【讨论】:

  • 我看不出使用Base 进行参数化有什么帮助,我的问题是关于删除using Base.../typedef Base...
  • 我不明白,我虽然你想在实例化派生类时定义基类类型。如果不是这样,我认为这个问题还不够清楚。有了这个,您的派生字段取决于您的基础字段,并在创建派生时实例化。这不是您要找的吗?
  • 如果我不够清楚,请原谅!我在问题中澄清了。为您的想法 +1。
  • 我很抱歉造成混乱。在您的情况下,也许您可​​以尝试双重继承,一个来自您不想在代码中重复的非泛型结构,另一个来自 Base。这实际上取决于您想要在派生的 Base 中使用的东西列表。
【解决方案3】:

这并不能回答问题,但如果您确实允许 Base 的专业化(这...您确实必须这样做),那么行为会变得非常奇怪。

考虑一下这个例子...

template<class T> struct Base {
  typedef T type;
  static const int n = 3;
  virtual int f() = 0;
  int f(int x) { return x * 2; }
};

typedef float type;
static const int n = 5;

template<class T> struct Derived : Base<T> {
  type field;
  int f() { return n; }
};

它可能不直观,但至少代码是可预测的。 Derived::field 始终是浮点数,而 Derived::f() 始终返回 5。

如果我们以某种方式欺骗编译器使用 Base 的每个成员,那么以一种奇怪的方式专门化 Base 会导致 Derived 以非常难以确定的方式运行,何时应该出错。

【讨论】:

  • 模板的目的是允许您删除的行为。是的,它允许“Derived 以一种非常难以确定的方式运行”,但这是元编程的代价。在仍然使用泛型的同时,还有其他限制开发人员的方法。
  • 我知道...我想我真正追求的是某种“接口”,所有 Base 特化都必须符合(类型 typedef、值 n 等)并且可能是默认在查找范围内。也许 C++ 概念可以解决这个问题?
猜你喜欢
  • 2021-12-31
  • 1970-01-01
  • 2011-09-20
  • 2020-10-13
  • 2016-02-22
  • 1970-01-01
  • 2015-11-15
  • 1970-01-01
  • 2011-09-12
相关资源
最近更新 更多