3

问题陈述(出于教育目的):
- 实现方法 printContainer,它适用于 STL容器vectorstack和. queuedeque

我做了一个解决方案,但由于代码量过多,我不喜欢它。
我为解决该问题所做的工作:
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);
4

3 回答 3

3

这个解决方案:

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,并且top()并非所有容器都提供函数(例如vector)。

考虑这个更简单的例子来解释:

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
}

在这里,我将一个在编译时已知的布尔模板参数传递给 function f()true如果输入是类型,我将通过X,因此支持一个名为foo();的成员函数。false如果输入的类型是 ,我将通过Y,因此支持名为 的成员函数bar()

即使选择适用于编译时已知的布尔值,语句本身也会在运行时执行。编译器首先必须编译整个函数,包括语句的false分支。if

您正在寻找的是某种static if结构,不幸的是,它在 C++ 中不可用。

这里的传统解决方案是基于重载的,实际上看起来就像您最初提供的解决方案。

于 2013-03-24T15:29:54.180 回答
2

我会反其道而行之。我会编写一个使用迭代器的通用函数:

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());
}

然后破解 aqueue或 a的容器stack

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());
}
于 2013-03-24T20:00:49.967 回答
1

虽然安迪的回答已经是一个很好的答案,但我想对您的实施添加一项改进。您可以对其进行改进以支持更多容器专业化,因为您的重载不允许 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::vector第二个参数为 s 的那些 s std::allocator<T>

虽然您可以手动向代码中添加更多参数,但还有一个更好的选择:可变参数模板。您可以将以下代码用于所有std::vectors 的真正通用版本:

template <typename... Ts>
typename vector<Ts...>::value_type top(const vector<Ts...>& v)
{
    return v.back();
}

当然,您可以对所有其他容器使用相同的技术,而不必担心它们具有的模板参数的确切数量。有些容器甚至有多达五个模板参数,所以如果你不使用可变参数模板,这可能会很烦人。

一个警告:一些较旧的编译器可能不喜欢可变参数版本,您必须手动迭代所有参数。

于 2013-03-24T22:41:59.907 回答