【问题标题】:C++ template class inheritC++模板类继承
【发布时间】:2017-07-14 04:00:04
【问题描述】:

如何定义一个模板类继承自模板类?

我想将std::queuestd::priority_queue 包装到一个基类中。在我的情况下是LooperQueue。 我这样使用StdQueueauto queue = new StdQueue<LooperMessage *>()

我的类定义编译器抱怨

错误日志:

  In file included from /Users/rqg/ASProjects/PboTest/muses/src/main/cpp/Painter.cpp:10:
  /Users/rqg/ASProjects/PboTest/muses/src/main/cpp/util/StdQueue.h:14:5: error: unknown type name 'size_type'; did you mean 'size_t'?
      size_type size() override;
      ^~~~~~~~~
      size_t
  /Users/rqg/Library/Android/sdk/ndk-bundle/toolchains/llvm/prebuilt/darwin-x86_64/lib64/clang/5.0.300080/include/stddef.h:62:23: note: 'size_t' declared here
  typedef __SIZE_TYPE__ size_t;
                        ^
  In file included from /Users/rqg/ASProjects/PboTest/muses/src/main/cpp/Painter.cpp:10:
  /Users/rqg/ASProjects/PboTest/muses/src/main/cpp/util/StdQueue.h:16:5: error: unknown type name 'reference'
      reference front() override;
      ^
  /Users/rqg/ASProjects/PboTest/muses/src/main/cpp/util/StdQueue.h:20:21: error: unknown type name 'value_type'; did you mean 'ARect::value_type'?
      void push(const value_type &x) override;
                      ^~~~~~~~~~
                      ARect::value_type
  /Users/rqg/Library/Android/sdk/ndk-bundle/sysroot/usr/include/android/rect.h:44:21: note: 'ARect::value_type' declared here
      typedef int32_t value_type;

代码:

#ifndef PBOTEST_LOOPERQUEUE_H
#define PBOTEST_LOOPERQUEUE_H

#include <queue>
#include <cstdlib>

template<typename Tp, typename Sequence = std::deque<Tp> >
class LooperQueue {
public:

    typedef typename Sequence::value_type                value_type;
    typedef typename Sequence::reference                 reference;
    typedef typename Sequence::const_reference           const_reference;
    typedef typename Sequence::size_type                 size_type;
    typedef          Sequence                            container_type;


    virtual size_type size()  = 0;

    virtual reference front() = 0;

    virtual void pop()= 0;

    virtual void push(const value_type &x) = 0;
};


#endif //PBOTEST_LOOPERQUEUE_H
 #ifndef PBOTEST_STDQUEUE_H
#define PBOTEST_STDQUEUE_H


#include "LooperQueue.h"

template<typename Tp, typename Sequence = std::deque<Tp> >
class StdQueue : public LooperQueue<Tp, Sequence> {
public:
    size_type size() override;

    reference front() override;

    void pop() override;

    void push(const value_type &x) override;

public:


private:
    std::queue<Tp, Sequence> mQueue;
};


#endif //PBOTEST_STDQUEUE_H

【问题讨论】:

  • 发布没有文件名和行号的编译器错误很臭。另外,我建议使用 'using' 而不是 typedef。语法更清晰,我认为它有时也可以提供更好的错误消息。
  • 另外,您不能使用以下划线后跟大写字母开头的名称,或任何以下划线开头的名称。例如,_Sequence 是不允许的。 stackoverflow.com/a/228797/493106
  • @xaxxon 任何带有两个连续下划线的标识符 anywhere 都是不允许的
  • @xaxxon 对我的错误日志帖子感到抱歉。我删除了下划线,但错误保持不变。我不明白“using 而不是 typedef”。有没有更详细的

标签: c++ class templates inheritance name-lookup


【解决方案1】:

我将使用一个更简单的示例来给出相同的错误,考虑一个只定义了一个别名的基类和一个尝试使用它的子类:

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

template <typename T>
class Derived : public Base<T> {
  value_type func();  // error
};

由于模板的疯狂性质,编译器此时无法知道value_type 是什么。您必须通过限定它来告诉它它来自 Base 类:

template <typename T>
class Derived : public Base<T> {
  typename Base<T>::value_type func();
};

或通过using 声明告诉编译器您打算使用基类类型别名

template <typename T>
class Derived : public Base<T> {
  using typename Base<T>::value_type;
  value_type func();
};

编译器实际上无法知道Base&lt;T&gt; 包含value_type,直到它知道T 是什么并实例化模板。 为什么它不能只看 Base 模板? -- 这样的事情在理论上是可能的,但它不知道会有哪些专业化可用。如果你有其他地方

template<>
class Base<int> {};

然后Derived&lt;int&gt; 将不得不在其范围内的其他地方寻找value_type,这就是它在您的原始代码中所做的。它试图找到value_type 并失败了。这种行为可能会导致一些令人惊讶的结果:

using value_type = char;
template <typename T>
class Derived : public Base<T> {
  value_type func(); // this is the global value_type = char, always
};

有关相关主题的更深入的解释you can read my medium post

【讨论】:

    【解决方案2】:

    这里的问题是基类LooperQueue是一个依赖基类,它依赖于模板参数TpSequence,那么在不知道模板参数的情况下无法确定它的完整类型。标准 C++ 表示不会在依赖基类中查找非依赖名称(如 size_typereferencevalue_type)。

    要更正代码,只需使名称符合基类名称即可;那么这些名称只能在实例化时查找,届时将知道必须探索的确切基础特化。例如

    template<typename _Tp, typename _Sequence = std::deque<_Tp> >
    class StdQueue : public LooperQueue<_Tp, _Sequence> {
    public:
        typename LooperQueue<_Tp, _Sequence>::::size_type size() override;
        typename LooperQueue<_Tp, _Sequence>::reference front() override;
        void pop() override;
        void push(const typename LooperQueue<_Tp, _Sequence>::value_type &__x) override;
    private:
        std::queue<_Tp, _Sequence> mQueue;
    };
    

    【讨论】:

    【解决方案3】:

    大多数(如果不是全部)编译错误是由于在派生类的名称查找期间未检查基类中的类型。 C++ 标准说您应该完全限定类型名称(请参阅this question)。换句话说,模板基类中的类型在没有完全限定的情况下从派生类中是不可见的。下面是一个使用g++-6.3.0 -std=c++14编译和运行的简单示例:

    #include <iostream>
    #include <deque>
    
    using namespace std;
    
    template <typename T, typename S = deque<T> >
    class Base
    {
    public:
        typedef typename S::size_type size_type;
    
        virtual size_type size() = 0;
    };
    
    
    template <typename T, typename S = deque<T> >
    class MyClass : public Base<T, S>
    {
    public:
        // type name has to be fully qualified
        typedef typename Base<T,S>::size_type size_type;
    
        // you could use "typename Base<T,S>::size_type" here instead
        size_type size() override { return 0; }
    };
    
    
    int main()
    {
        MyClass<int> c;
        cout << c.size() << endl;
    }
    

    【讨论】:

    • typedef typename Base&lt;T,S&gt;::size_type size_type; 可以用using typename Base&lt;T,S&gt;::size_type;完成
    • 是的,所有的typedefs 都可以用using(别名声明)来完成,我更喜欢使用别名声明而不是typedef。但是,我决定留下typedefs 以使示例更接近Fantasy_RQG 的原始代码。
    • 您的代码运行良好,但我无法将函数定义拆分为声明和定义。有没有办法做到这一点?
    • 要将函数声明与定义分开,首先将定义留在类中。它看起来像这样:size_type size() override; 然后,在类之后或在.C 文件中:template &lt;typename T, typename S&gt; typename MyClass&lt;T,S&gt;::size_type MyClass&lt;T,S&gt;::size() { return 0; } 当然,您可以将定义写在不同的行上。如果您希望我编辑答案中的代码以反映这一点,请告诉我。
    • 顺便说一句,如果您将模板定义放在.C.cpp 文件中,则需要显式实例化特定类型的模板类,这是限制(和不同的主题)。我通常只是将定义放在.h 文件中。
    猜你喜欢
    • 2012-10-05
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多