C++ 模板 class 继承
C++ template class inherit
如何定义模板 class 从模板 class 继承?
我想将 std::queue
和 std::priority_queue
包装到基数 class 中。在我的例子中是 LooperQueue
。
我这样用StdQueue
auto queue = new StdQueue<LooperMessage *>()
.
我的class定义编译器抱怨
错误日志:
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
我将使用一个更简单的示例来给出相同的错误,考虑一个只定义了一个别名的基和一个尝试使用它的子class:
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 class:
template <typename T>
class Derived : public Base<T> {
typename Base<T>::value_type func();
};
或使用 using
声明告诉编译器您打算使用基本 class 类型别名
template <typename T>
class Derived : public Base<T> {
using typename Base<T>::value_type;
value_type func();
};
编译器实际上并不知道Base<T>
包含一个value_type
,直到它知道T
是什么并实例化模板。 为什么它不能只看 Base 模板? -- 这样的事情在理论上是可能的,但它不知道有哪些专业化可用。如果你在其他地方有
template<>
class Base<int> {};
然后 Derived<int>
将不得不在其范围内的其他地方寻找 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
大多数(如果不是全部)编译错误是由于在派生 class 的名称查找期间未检查基 class 中的类型。 C++ 标准规定您应该完全限定类型名称(请参阅 this question)。换句话说,模板基 class 中的类型在派生的 class 中不可见,除非它们是完全合格的。下面是一个使用 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;
}
这里的问题是基classLooperQueue
是一个依赖基class,它依赖于模板参数Tp
和Sequence
,那么在不知道模板参数的情况下无法确定其完整类型。标准 C++ 规定非依赖名称(如 size_type
、reference
和 value_type
)不会在依赖基 classes.
中查找
要更正代码,只需使名称符合基本 class 名称即可;那么只能在实例化时查找这些名称,并且那时将知道必须探索的确切基础专业化。例如
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;
};
如何定义模板 class 从模板 class 继承?
我想将 std::queue
和 std::priority_queue
包装到基数 class 中。在我的例子中是 LooperQueue
。
我这样用StdQueue
auto queue = new StdQueue<LooperMessage *>()
.
我的class定义编译器抱怨
错误日志:
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
我将使用一个更简单的示例来给出相同的错误,考虑一个只定义了一个别名的基和一个尝试使用它的子class:
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 class:
template <typename T>
class Derived : public Base<T> {
typename Base<T>::value_type func();
};
或使用 using
声明告诉编译器您打算使用基本 class 类型别名
template <typename T>
class Derived : public Base<T> {
using typename Base<T>::value_type;
value_type func();
};
编译器实际上并不知道Base<T>
包含一个value_type
,直到它知道T
是什么并实例化模板。 为什么它不能只看 Base 模板? -- 这样的事情在理论上是可能的,但它不知道有哪些专业化可用。如果你在其他地方有
template<>
class Base<int> {};
然后 Derived<int>
将不得不在其范围内的其他地方寻找 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
大多数(如果不是全部)编译错误是由于在派生 class 的名称查找期间未检查基 class 中的类型。 C++ 标准规定您应该完全限定类型名称(请参阅 this question)。换句话说,模板基 class 中的类型在派生的 class 中不可见,除非它们是完全合格的。下面是一个使用 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;
}
这里的问题是基classLooperQueue
是一个依赖基class,它依赖于模板参数Tp
和Sequence
,那么在不知道模板参数的情况下无法确定其完整类型。标准 C++ 规定非依赖名称(如 size_type
、reference
和 value_type
)不会在依赖基 classes.
要更正代码,只需使名称符合基本 class 名称即可;那么只能在实例化时查找这些名称,并且那时将知道必须探索的确切基础专业化。例如
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;
};