C++ 如何使用template参数为没有该方法所需的公共接口的STL容器实现通用方法
问题陈述(出于教育目的):C++ 如何使用template参数为没有该方法所需的公共接口的STL容器实现通用方法,c++,templates,stl,c++11,C++,Templates,Stl,C++11,问题陈述(出于教育目的): -实现方法printContainer,该方法适用于STL容器vector、stack、queue和deque 我提出了一个解决方案,但由于代码太多,我不喜欢它。 我解决问题的方法: 1.设计了一个通用函数,该函数期望容器为操作提供统一的接口:获取最后一个元素的值并从容器中删除该元素 模板 无效打印容器(T容器) { 不能此溶液: template <typename T, typename Base, template<typename T, class
-实现方法printContainer,该方法适用于STL容器
vector
、stack
、queue
和deque
我提出了一个解决方案,但由于代码太多,我不喜欢它。我解决问题的方法:
1.设计了一个通用函数,该函数期望容器为操作提供统一的接口:获取最后一个元素的值并从容器中删除该元素
模板
无效打印容器(T容器)
{
不能此溶液:
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();
}
这里,我向函数f()
传递编译时已知的布尔模板参数。如果输入类型为X
,因此支持名为foo()的成员函数,则传递true
;如果输入类型为Y
,则传递false
,因此支持名为bar()
的成员函数
即使选择在编译时已知的布尔值上工作,语句本身也在运行时执行。编译器首先必须编译整个函数,包括if
语句的false
分支
你所寻找的是某种类型的,很不幸的是C++中没有。
这里的传统解决方案是基于重载的,事实上看起来就像您最初提供的解决方案一样。我的想法是相反的。我将编写一个使用迭代器的通用函数:
template <class Iter>
void show_contents(Iter first, Iter last) {
// whatever
}
然后定义专门化以创建这些对象并显示其内容:
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());
}
模板
void show_容器(const stack&s){
黑客攻击;
显示内容(hack.begin(),hack.end());
}
模板
void show_容器(const queue&q){
hack-hack(q);
显示内容(hack.begin(),hack.end());
}
虽然Andy的答案已经很好了,但我想对您的实现做一点改进。您可以对其进行改进,以支持更多的容器专门化,因为您的重载不允许STL容器必须是非默认的所有模板参数。例如,请看您的代码:
template <typename T>
typename vector<T>::value_type top(const vector<T>& v)
{
return v.back();
}
当然,您可以对所有其他容器使用相同的技术,而不必担心它们拥有的模板参数的确切数量。有些容器甚至有多达个,因此如果您不使用可变模板,这可能会非常烦人
一个警告:一些旧的编译器可能不喜欢变量版本,您必须手动迭代所有参数。在上一个示例中,第二个函数模板不应该命名为pop()
而不是top()
?是的,没错,谢谢-应该是pop()@是的,我注意到了。但这并不重要,问题还不止于此(请参阅答案以获得澄清)。我现在正在查看它,我需要一些时间来理解其中提到的想法。“这意味着所有分支的代码都必须编译”-我认为它们是在实例化模板时编译的。“并且并非所有容器(例如vector)都提供函数top()”-我实现top(vector)的原因是什么对不起,我没有从您那里得到答案,为什么带有模板参数的想法不能应用于我的task@spin_eight:事实上,它们是在实例化模板时编译的,但是编译了if
语句的所有分支。这就是我回答中的示例试图阐明的。你不能说“如果是向量,则调用此函数,否则调用另一个函数”,因为当传递向量时,编译器必须编译“if is vector”分支和“otherwise”分支“分支,两个中的一个将不会编译。感谢您的清晰和良好的解释,我明白了。因此,为了检查我是否正确:当我调用向量模板的函数时,应该实例化该模板,因此编译器应该为该模板内使用的所有对象和函数生成代码,但因为没有top()和front方法()在矢量代码中无法为它们生成,这是主要问题。感谢您提供的信息。“由于您的重载不允许所有模板参数,因此STL容器必须是非默认的”-我认为这将足以使我的模板函数适用于所有基容器(列表、矢量、deque)因为第二个参数是allocator,在我的函数中根本没有使用,这意味着我的函数不需要从模板中推断它例如,我的函数使用该分配器为vector工作,这意味着使用作为分配器的第二个模板参数不会限制我的函数使用范围,但情况并非如此,尽管还有其他因素限制了它。@spin_八:你的std::vector
重载将不匹配std::vector
如果是的话,那是一个严重的编译器错误。至少,我是这样读你的评论的。如果你只是说你不需要它,好吧。是的,它是匹配的,如果它是一个bug,那么这种行为应该与C++标准相矛盾,请你指出我的矛盾。@ Daniel Frey。在对我的代码进行了一些实验之后,我得出结论,您是对的。感谢您指出ommited 2-nd template参数的问题,现在我了解了它的工作原理和可能出现的问题。谢谢,我喜欢这个想法(使用底层容器并使用它们提供的迭代器)非常喜欢。最初我打算使用底层容器,而不是适配器,因为这样只允许处理3个容器(list、vector、deque)。如果我决定使用适配器,将会有更多的工作要做。但我拒绝了我的想法
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();
}
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
}
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());
}
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());
}
template <typename T>
typename vector<T>::value_type top(const vector<T>& v)
{
return v.back();
}
template <typename... Ts>
typename vector<Ts...>::value_type top(const vector<Ts...>& v)
{
return v.back();
}