可变参数模板 Class 未完全展开
Variadic Template Class not fully expanded
我正在尝试创建类似于类型安全多队列的东西。这个想法是,当我推送一个项目时,它将被添加到一个由相同类型的对象组成的队列中。
所有队列都有一个通用接口(在本例中为 queue_intf
),可以进行一些处理。
我想出的代码如下:
#include <queue>
#include <vector>
#include <memory>
#include <stdlib.h>
class queue_intf {
public:
virtual void process(void) = 0;
};
template <typename T>
class queue : public queue_intf {
public:
std::queue<T> q_;
static const char* name()
{
return typeid(T).name();
}
virtual void process(void) override {
printf("process: %s\n", this->name());
}
void push(T &a) {
printf("push: %s\n", this->name());
}
};
template <typename...>
class queues {
public:
std::vector<queue_intf *> qs_;
void process(void) {
for (auto q: this->qs_) {
q->process();
}
}
};
template <typename T, typename... Ts>
class queues<T, Ts...> : public queues<Ts...> {
public:
queue<T> q_;
queues() {
this->qs_.push_back(&this->q_);
}
void push(T &v) {
q_.push(v);
}
};
class a {
};
class b {
};
int
main (int argc, char *argv[])
{
queues<a, b> qs;
a ai;
b bi;
qs.push(ai);
qs.process();
qs.push(bi);
}
但是当我编译它时出现以下错误:
main.cc: In function ‘int main(int, char**)’:
main.cc:65:15: error: no matching function for call to ‘queues<a, b>::push(b&)’
qs.push(bi);
^
main.cc:45:10: note: candidate: void queues<T, Ts ...>::push(T&) [with T = a; Ts = {b}]
void push(T &v) {
^~~~
main.cc:45:10: note: no known conversion for argument 1 from ‘b’ to ‘a&’
我期待 queue
class 有 void push(b &v)
方法,但似乎没有。
知道为什么吗?
编辑:
这是一个较小的例子(没有 std:vector
或任何东西):
template <typename...>
class queues {
};
template <typename T, typename... Ts>
class queues<T, Ts...> : public queues<Ts...> {
public:
void push(T &v) {
}
};
class a {
};
class b {
};
int
main (int argc, char *argv[])
{
queues<a, b> qs;
a ai;
b bi;
qs.push(ai);
qs.push(bi);
}
为了解释这一点,让我们使用一个简化的例子,你只有两个 classes:
class base {
public:
void method(int *);
};
class derived : public base {
public:
void method(char *);
};
你试图调用它:
derived d;
int i;
d.method(&i);
这失败了。名称查找从给定的 derived
class 开始,并找到具有指定名称的匹配 class 方法:method
。但是它的参数不匹配,所以这是 ill-formed。如果 derived
class 有两种方法,重载解析将选择匹配的一个。但是一旦找到具有指定名称的 class 方法,将不会搜索任何父 class 以查找具有相同名称的任何其他方法。仅当派生 class 没有任何具有指定名称的方法时,名称查找才会继续使用父 class(es).
放
using base::method;
在派生的 class 中将基的 method
导入派生的命名空间,此时一切都像常规重载解析一样工作。
这就是为什么您的递归模板需要 using
声明。第二个派生模板从第一个派生模板导入它,下一个派生模板从第二个派生模板导入所有内容,依此类推。
我正在尝试创建类似于类型安全多队列的东西。这个想法是,当我推送一个项目时,它将被添加到一个由相同类型的对象组成的队列中。
所有队列都有一个通用接口(在本例中为 queue_intf
),可以进行一些处理。
我想出的代码如下:
#include <queue>
#include <vector>
#include <memory>
#include <stdlib.h>
class queue_intf {
public:
virtual void process(void) = 0;
};
template <typename T>
class queue : public queue_intf {
public:
std::queue<T> q_;
static const char* name()
{
return typeid(T).name();
}
virtual void process(void) override {
printf("process: %s\n", this->name());
}
void push(T &a) {
printf("push: %s\n", this->name());
}
};
template <typename...>
class queues {
public:
std::vector<queue_intf *> qs_;
void process(void) {
for (auto q: this->qs_) {
q->process();
}
}
};
template <typename T, typename... Ts>
class queues<T, Ts...> : public queues<Ts...> {
public:
queue<T> q_;
queues() {
this->qs_.push_back(&this->q_);
}
void push(T &v) {
q_.push(v);
}
};
class a {
};
class b {
};
int
main (int argc, char *argv[])
{
queues<a, b> qs;
a ai;
b bi;
qs.push(ai);
qs.process();
qs.push(bi);
}
但是当我编译它时出现以下错误:
main.cc: In function ‘int main(int, char**)’:
main.cc:65:15: error: no matching function for call to ‘queues<a, b>::push(b&)’
qs.push(bi);
^
main.cc:45:10: note: candidate: void queues<T, Ts ...>::push(T&) [with T = a; Ts = {b}]
void push(T &v) {
^~~~
main.cc:45:10: note: no known conversion for argument 1 from ‘b’ to ‘a&’
我期待 queue
class 有 void push(b &v)
方法,但似乎没有。
知道为什么吗?
编辑:
这是一个较小的例子(没有 std:vector
或任何东西):
template <typename...>
class queues {
};
template <typename T, typename... Ts>
class queues<T, Ts...> : public queues<Ts...> {
public:
void push(T &v) {
}
};
class a {
};
class b {
};
int
main (int argc, char *argv[])
{
queues<a, b> qs;
a ai;
b bi;
qs.push(ai);
qs.push(bi);
}
为了解释这一点,让我们使用一个简化的例子,你只有两个 classes:
class base {
public:
void method(int *);
};
class derived : public base {
public:
void method(char *);
};
你试图调用它:
derived d;
int i;
d.method(&i);
这失败了。名称查找从给定的 derived
class 开始,并找到具有指定名称的匹配 class 方法:method
。但是它的参数不匹配,所以这是 ill-formed。如果 derived
class 有两种方法,重载解析将选择匹配的一个。但是一旦找到具有指定名称的 class 方法,将不会搜索任何父 class 以查找具有相同名称的任何其他方法。仅当派生 class 没有任何具有指定名称的方法时,名称查找才会继续使用父 class(es).
放
using base::method;
在派生的 class 中将基的 method
导入派生的命名空间,此时一切都像常规重载解析一样工作。
这就是为什么您的递归模板需要 using
声明。第二个派生模板从第一个派生模板导入它,下一个派生模板从第二个派生模板导入所有内容,依此类推。