如何使用模板模板参数实现没有通用接口的STL容器的通用方法
How to implement generic method for STL containers that haven`t common interface needed for that method using template template parameter
问题陈述(用于教育目的):
-实现方法printContainer适用于STL容器vector
, stack
, queue
和deque
。
我做了一个解决方案,但我不喜欢它,因为代码太多了。
我是怎么解决这个问题的:
1。设计了一个通用函数,它要求容器提供统一的接口,用于以下操作:获取最后一个元素的值并从容器中删除
template <typename T>
void printContainer(T container)
{
cout << " * * * * * * * * * * " << endl;
cout << " operator printContainer(T container). Stack, queue, priority queue"
<< endl;
cout << typeid(container).name() << endl;
while (!container.empty())
{
cout << top(container) << " ";
pop(container);
}
cout << endl;
cout << " * * * * * * * * * * * " << endl;
}
对于每个容器,我实现了允许提供统一接口的函数(我想重构下面的代码片段):
template <typename T>
typename vector<T>::value_type top(const vector<T>& v)
{
return v.back();
}
template <typename T, typename Base>
typename stack<T, Base>::value_type top(const stack<T, Base>& s)
{
return s.top();
}
template <typename T, typename Base>
typename queue<T, Base>::value_type top(const queue<T, Base>& q)
{
return q.front();
}
template <typename T, typename Base>
typename priority_queue<T, Base>::value_type top(const priority_queue<T,
Base>& pq)
{
return pq.top();
}
template <typename T>
void pop(vector<T>& v)
{
return v.pop_back();
}
template <typename T, typename Base>
void pop(stack<T, Base>& s)
{
return s.pop();
}
template <typename T, typename Base>
void pop(queue<T, Base>& q)
{
return q.pop();
}
template <typename T, typename Base>
void pop(priority_queue<T,Base>& pq)
{
return pq.pop();
}
我不想用这样的东西来代替它:
template <typename T, typename Base, template<typename T, class Base,
class ALL = std::allocator<T>> class container>
typename container<T,Base>::value_type top(container<T,Base>& c)
{
if (typeid(container).name == typeid(vector<T,Base>))
return c.back();
if (typeid(container).name == typeid(queue<T,Base>))
return c.front();
else
return c.top();
}
template <typename T, typename Base, template<typename T, class Base,
class ALL = std::allocator<T>> class container>
typename container<T,Base>::value_type pop(container<T,Base>& c)
{
if (typeid(container).name == typeid(vector<T,Base>))
c.pop_back();
else
return c.pop();
}
但是它不起作用,我得到这样的错误:
Error 1 error C2784: 'container<T,Base>::value_type top(container<T,Base> &)' : could not deduce template argument for 'container<T,Base> &' from 'std::stack<_Ty>'
问题:
我应该在模板模板参数中做邻接来整理错误,可能有我忽略的东西或者存在逻辑错误。
无论如何,欢迎任何有用的信息。
提前感谢!
更新:
//这就是我试图调用函数
的方式int arr[] = {1,2,3,4,5,6,7,8,9,0};
stack<int> s(deque<int>(arr, arr + sizeof(arr) / sizeof(arr[0])));;
queue<int> q(deque<int>(arr, arr + sizeof(arr) / sizeof(arr[0])));
priority_queue<int> pq(arr, arr + sizeof(arr) / sizeof(arr[0]));
printContainer(s);
printContainer(q);
printContainer(pq);
解决方案:
template <typename T, typename Base, template<typename T, class Base,
class ALL = std::allocator<T>> class container>
typename container<T,Base>::value_type top(container<T,Base>& c)
{
if (typeid(container).name == typeid(vector<T,Base>))
return c.back();
if (typeid(container).name == typeid(queue<T,Base>))
return c.front();
else
return c.top();
}
不起作用,因为if()
实现了一个运行时选择,这意味着所有分支的代码都必须编译,即使其中只有一个计算结果为true
,并且并不是所有容器(例如vector
)都提供top()
函数。
考虑下面这个更简单的例子作为解释:
struct X { void foo() { } };
struct Y { void bar() { } };
template<bool b, typename T>
void f(T t)
{
if (b)
{
t.foo();
}
else
{
t.bar();
}
}
int main()
{
X x;
f<true>(x); // ERROR! bar() is not a member function of X
Y y;
f<false>(y); // ERROR! foo() is not a member function of Y
}
这里,我将一个布尔模板参数传递给函数f()
,该参数在编译时是已知的。如果输入类型为X
,则传递true
,因此支持称为foo()
的成员函数;如果输入类型为Y
,则传递false
,因此支持称为bar()
的成员函数。
尽管选择在编译时已知的布尔值上工作,但语句本身在运行时执行。编译器首先必须编译整个函数,包括if
语句的false
分支。
你正在寻找的是某种static if
结构,不幸的是在c++中不可用。
这里的传统解决方案基于重载,实际上与您最初提供的解决方案类似。
如果是我,我会用另一种方式来处理。我会写一个使用迭代器的泛型函数:
template <class Iter>
void show_contents(Iter first, Iter last) {
// whatever
}
然后是接受容器的泛型函数:
template <class Container>
void show_container(const Container& c) {
show_contents(c.begin(), c.end());
}
然后是获取queue
或stack
底层容器的hack:
template <class C>
struct hack : public C {
hack(const C& cc) : C(cc) { }
typename C::Container::const_iterator begin() const {
return this->c.begin();
}
typename C::Container::const_iterator end() const {
return this->c.end();
}
};
然后定义专门化来创建这些对象并显示其内容:
template <class T>
void show_container(const stack<T>& s) {
hack<stack<T>> hack(s);
show_contents(hack.begin(), hack.end());
}
template <class T>
void show_container(const queue<T>& q) {
hack<stack<T>> hack(q);
show_contents(hack.begin(), hack.end());
}
虽然Andy的回答已经很好了,但我想对您的实现添加一个改进。您可以改进它以支持更多的容器专门化,因为您的重载不允许STL容器必须是非默认的所有模板参数。例如,看看你的代码:
template <typename T>
typename vector<T>::value_type top(const vector<T>& v)
{
return v.back();
}
,现在与std::vector
的定义进行比较。类模板有两个参数,即std::vector<T,Allocator=std::allocator<T>>
。重载只接受第二个形参为std::allocator<T>
的std::vector
。
虽然您可以手动向代码中添加更多参数,但有一个更好的选择:可变模板。您可以使用以下代码获得所有std::vector
s的真正通用版本:
template <typename... Ts>
typename vector<Ts...>::value_type top(const vector<Ts...>& v)
{
return v.back();
}
,当然,您可以对所有其他容器使用相同的技术,并且不需要担心它们拥有的模板参数的确切数量。有些容器甚至有多达5个模板参数,所以如果你不使用可变模板,这可能会很烦人。
一个警告:一些旧的编译器可能不喜欢可变的版本,你必须手动迭代所有的参数。
- "operator()"在重载运算符方法中是什么意思,在priority_queue(STL)中用作C++中的比较器?
- C++ STL 容器中优先级搜索的优雅方法
- 有没有一种 STL 方法可以找到字符串的所有排列,给出一个以 C++ 为单位的大小?
- STL vector.insert 方法期望_InputIterator作为参数
- 是否有任何常规方法可以通知 STL 移动和复制构造函数?
- 遍历 STL 映射(集/多集)的最佳方法,同时元素可能会在循环期间被删除并重新插入?
- 仅编写 c++ stl 向量的第一个元素的正确方法
- 为 STL 随机数生成器编写工厂方法
- 哪些更好的方法可以显示使用C STL创建的堆栈而不弹出每个元素的方法
- 如果其他人在等待,是否有标准的STL或QT方法可以产生互惠码,否则请保留它
- 有没有一种 stl 方法来执行指针向量的深度复制
- 寻找一种更有效的方法来使用 STL 函数检查字符串是否为回文
- 方法插入到任何stl集合中
- STL 中的排序方法不能交换向量中的内容
- 有没有一种方法可以防止在STL无序映射上插入或擦除
- Eclipse CDT无法在STL容器中的元素上解析方法
- 现代过滤STL容器的方法
- 从字符串中提取子字符串的C++ stl 方法> 到 (如 Javascript 子字符串)
- 使用STL方法在c++中对多维数组进行排序
- 我在哪里可以找到有关c++ /STL方法异常保证的信息