简体   繁体   中英

C++ template class inherit

how to define a template class inherit from template class ?

I want to wrap std::queue and std::priority_queue to a base class. In my case is LooperQueue . I use StdQueue in this way auto queue = new StdQueue<LooperMessage *>() .

my class define compiler complain

error log:

  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;

code:

#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

I'm going to use a simpler example that gives you the same error, consider a base with just one alias defined and a subclass that tries to use it:

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

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

Because of the crazy nature of templates, the compiler can't know what value_type is at this point. You have to tell it that it comes from the Base class either by qualifying it:

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

or telling the compiler with a using declaration that you are intending to use a base class type alias

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

The compiler can't actually know that Base<T> contains a value_type until it knows what T is and instantiates the template. Why can't it just look at the Base template? -- such a thing would be theoretically possible, but it doesn't know what specializations will be available. If somewhere else you had

template<>
class Base<int> {};

Then Derived<int> would have to look for value_type elsewhere in its scope, and that's what it does in your original code. It tries to find a value_type and fails. This behavior can lead to some surprising results:

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

For a more ground-up explanation on related topics you can read my medium post

The issue here is that the base class LooperQueue is a dependent base class, which depends on the template parameter Tp and Sequence , then its complete type can't be determined without knowing the template arguments. Standard C++ says that nondependent names (like size_type , reference and value_type ) won't be looked up in dependent base classes.

To correct the code, it suffices to make the names qualified by the base class name; then these names can be looked up only at the time of instantiation, and at that time the exact base specialization that must be explored will be known. eg

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;
};

Most, if not all, of your compilation errors are due to the types in your base class not being examined during name lookup of your derived class. The C++ standard says that you should fully qualify the type names (see this question ). In other words, the types in your template base class are not visible from your derived class without their being fully qualified. Below is a simple example that compiles and runs with 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;
}

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM