【问题标题】:Why does std::stack not use template template parameter?为什么 std::stack 不使用模板模板参数?
【发布时间】:2016-08-02 13:20:14
【问题描述】:

为什么std::stackstd::queue 对其底层容器类型使用类型模板参数而不是模板模板参数?

即为什么stack 是这样声明的:

template<typename T, typename Container = deque<T>>
class stack;

但不是这样:

template<typename T, template<typename> class Container = deque>
class stack;

?

【问题讨论】:

  • @CoryKramer 这个问题不是链接问题的重复:链接的问题询问我们需要在哪里放置模板/类型名,而这个问题是关于为什么现有类 (deque)有一个特定的界面,而不是一个句法问题。
  • 默认值甚至适用于模板模板参数吗?
  • @anatolyg 是的,它们有效。

标签: c++ templates


【解决方案1】:

因为通常是像 std::vector have more than one template argument 这样的容器。通过不关心它是模板,您可以使用各种容器。

会怎样

template<class T, class Allocator = std::allocator<T>> class vector;

适合

template<typename> class Container

正如您在stack 中所拥有的那样? (提示:它没有!)对于您想要支持的每种数量和种类的模板参数(类型与非类型),您都需要特殊情况,这很愚蠢,因为这些通常不会提供任何帮助比简单的更多信息

typename Container

请注意,要获取实际的模板参数,例如std::vector,你有 typedefs std::vector::value_typestd::vector::allocator_type,不需要在你实际使用类型的地方显式地使用这些类型(即Containerstack)。

【讨论】:

  • 哎哟。本以为不用两次指定T就可以写std::stack&lt;T, std::vector&gt;会更方便,但没想到容器模板参数的限制。
  • @Hedede 还要考虑甚至没有从模板实例化的容器。使用您的设计,您无法执行 std::stack&lt;double, MySpecialContainerWhichOnlyWorksForDoubles&gt; 之类的操作。
  • 这比“每个 number 个模板参数的特殊情况”要糟糕得多,因为模板模板参数可能混合了类型和非类型模板参数。 IE。您不仅可以拥有Container&lt;Type1, Type2&gt;,还可以拥有Container&lt;Type1, Int2&gt;。这使得它成倍地变得更糟。
【解决方案2】:

简而言之:因为使用模板模板参数比使用类型参数更严格*而不提供任何优势。

* restrictive 我的意思是你可能需要更复杂的东西来获得与使用“简单”类型参数相同的结果。

为什么没有优势?

您的std::stack 可能具有这样的属性:

template <typename T, typename Container>
struct stack {
    Container container;
};

如果用模板模板参数替换Container,为什么会得到?

template <typename T, template <typename...> class Container>
struct stack {
    Container<T> container;
};

您只为T (Container&lt;T&gt;) 实例化Container 一次,因此模板模板参数没有优势

为什么限制更多?

使用模板模板参数,您必须将公开相同签名的模板传递给std::stack,例如:

template <typename T, template <typename> class Container>
struct stack;

stack<int, std::vector> // Error: std::vector takes two template arguments

也许你可以使用可变参数模板:

template <typename T, template <typename... > class Container>
struct stack {
    Container<T> container;
};

stack<int, std::vector> // Ok, will use std::vector<int, std::allocator<int>>

但是如果我不想使用标准的std::allocator&lt;int&gt; 怎么办?

template <typename T, 
          template <typename....> class Container = std::vector, 
          typename Allocator = std::allocator<T>>
struct stack {
    Container<T, Allocator> container;
};

stack<int, std::vector, MyAllocator> // Ok...

这变得有点乱了...如果我想使用自己的容器模板,它接受 3/4/N 个参数怎么办?

template <typename T,
          template <typename... > class Container = std::vector,
          typename... Args>
struct stack {
    Container<T, Args...> container;
};

stack<int, MyTemplate, MyParam1, MyParam2> // Ok...

但是,如果我想使用非模板容器怎么办?

struct foo { };
struct foo_container{ };

stack<foo, foo_container> // Error!

template <typename... >
using foo_container_template = foo_container;

stack<foo, foo_container_template> // Ok...

有了类型参数就不存在这样的问题1

stack<int>
stack<int, std::vector<int, MyAllocator<int>>
stack<int, MyTemplate<int, MyParam1, MyParam2>>
stack<foo, foo_container>

1 还有其他情况不适用于模板模板参数,例如使用接受特定顺序的类型和非类型参数混合的模板,您可以为此创建泛型template 模板参数,甚至使用可变参数模板。

【讨论】:

  • 我认为这根本不是正确答案:请参阅其他答案以了解正确的原因。
  • @KonradRudolph 在 c++11 中,您可以使用 template &lt;typename... &gt; class Container,它可以与模板模板参数一起使用。
  • @Holt 没错,很公平。也就是说,接口当然早于C++11,而接口设计的原因首先是pre-11 C++的技术限制。
  • @Holt 但是如果您使用可变参数模板模板,您仍然需要以某种方式获取参数,因此您需要std::stack 上的可变参数来填充容器的参数。
  • @TartanLlama 如果参数在给定模板中是默认的,则不是。
【解决方案3】:

使用模板模板参数会将可用作底层容器的类型限制为公开相同模板签名的类型。这种形式允许任意类型,只要它们支持预期的接口。

【讨论】:

    【解决方案4】:

    Because it doesn’t compile:

    std::deque 不是类型

    template <typename T> class std::deque
    

    它的类型

    template<class T, class Alloc> class std::deque
    

    这当然是一个更普遍的问题:即使我们要为 stack 类模板提供 Alloc 模板参数,该类现在也只能与恰好具有两个类型模板参数的容器一起使用。这是不合理的限制。

    【讨论】:

    • @Holt 这里有一个关于类型参数的很好的答案,允许使用非模板容器,如struct foo; struct foo_container; using foo_stack = std::stack&lt;foo, foo_container&gt;。但是在谈到现代标准时,template&lt;typename...&gt; 会允许使用这样的容器吗?即,可变参数模板可以“简化”为没有模板参数的简单类吗?
    • @Artalus 好问题。不,它不能。顺便说一句,他的回答得到了支持。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2019-11-16
    • 1970-01-01
    • 2020-10-13
    • 2011-10-01
    • 1970-01-01
    相关资源
    最近更新 更多