Reputation: 155
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;
private:
std::queue<Tp, Sequence> mQueue;
};
#endif //PBOTEST_STDQUEUE_H
Upvotes: 4
Views: 5259
Reputation: 36792
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
Upvotes: 8
Reputation: 172884
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. e.g.
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;
};
Upvotes: 3
Reputation: 1026
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;
}
Upvotes: 1