【问题标题】:How to declare a template function taking a template template parameter with a known template argument type?如何使用具有已知模板参数类型的模板模板参数声明模板函数?
【发布时间】:2014-04-02 14:05:03
【问题描述】:

我有以下两个功能:

class Leaf {...};
void SpitLeaves(std::string & sdata, std::vector<Leaf> const & leaves);
void SpitLeaves(std::string & sdata, std::set<Leaf> const & leaves);

这些函数的定义是相同的。

这是模板化函数的明显候选者。

但是,尽管进行了搜索,但我无法弄清楚如何正确声明模板函数。从https://stackoverflow.com/a/4697356/368896 和其他人,我尝试过签名,例如:

template <template<typename> class T>
void SpitLeaves(std::string & sdata, T<Leaf> const & leaves)
{...}

但是,这会在我尝试实例化模板函数时出现编译器错误:

std::string leaves_str;
std::vector<Leaf> leaves;
SpitLeaves<std::vector>(leaves_str, leaves);

...错误(VS 2013)是'SpitLeaves' : template parameter list for class template 'std::vector' does not match template parameter list for template template parameter 'T'

如何正确声明上述模板函数?

【问题讨论】:

    标签: c++ templates


    【解决方案1】:

    在 C++11 中,您可以使用可变参数模板模板参数:

    template <template<typename...> class T>
    void SpitLeaves(std::string & sdata, T<Leaf> const & leaves)
    

    现在编译器不会关心T可以带多少模板参数,只要T&lt;Leaf&gt;可以被实例化就行了。

    如果您确实想要支持具有零个或多个额外显式模板参数的容器,例如自定义分配器,您可以这样做:

    template <template<typename...> class T, typename... TX>
    void SpitLeaves(std::string & sdata, T<Leaf, TX...> const & leaves)
    

    【讨论】:

    • @Jarod42 好点,已修复。我看到你在回答中已经做了同样的事情。
    【解决方案2】:

    你可以选择:

    template<typename Container>
    void SpitLeaves(std::string & sdata, Container const & leaves) { ... }
    

    这样称呼它:

    std::vector<Leaf> leaves;
    SpitLeaves("leaves", leaves); // template argument not necessary
    

    您的模板模板方法的问题是容器可能只接受一个以上的参数。例如vectortakes two:要存储的元素类型和要使用的分配器。所以为了工作,你需要这个:

    template< template<typename,typename> class T, typename A >
    void SpitLeaves(std::string & sdata, T<Leaf,A> const & leaves) { ... }
    

    如果你使用泛型类型Container,你可以直接使用容器,如果它与你的函数不兼容,编译器会报错。

    【讨论】:

    • @DanNissenbaum,这样的执法有什么好处?
    • 通过查看函数签名,可以更及时的掌握函数的目的是什么。另外,我想知道如何在我使用模板时实现这种类型的粒度,因为我怀疑它将来可能会有用。我想知道 C++11 是否有可能!
    • 通过强制使用Leaf,它还可以防止任何不希望的副作用,如果它与不是Leaf 的东西一起使用,即使接口允许它构建。
    • 谢谢。然后是一个后续问题:在 C++11 中,是否有可能利用默认模板参数,以便能够为 SpitLeaves 提供不需要明确要求的声明的替代版本提供第二个模板参数?
    • @user2079303 我大体上同意。但是,如果需要更大的通用性,则可以在那时轻松添加。这种做法符合“高粒度更容易构建,然后再降低粒度,比反过来更容易”的概念。但无论如何,我只想知道如何以一种简单、巧妙的方式做到这一点,或者另一方面,我想知道没有简单、灵活的方式去做。
    【解决方案3】:

    std::vectorstd::set 有多个模板参数。

    所以你应该使用类似的东西:

    template <template<typename...> class C, typename ... Args>
    void SpitLeaves(std::string & sdata, const C<Leaf, Args...>& leaves)
    

    另一种方法是取容器,并使用 SFINAE 来限制正确的类型:

    template <typename C>
    typename std::enable_if<std::is_same<Leaf, typename C::value_type>::value>::type
    SpitLeaves(std::string & sdata, const C& leaves)
    

    【讨论】:

    • 您也可以将is_same 条件移动到函数体中的static_assert 中,而不是SFINAE。恕我直言,它更易于阅读,并且会生成更好的错误消息。
    • @Oktalist 除非有另一个用于非容器的 SpitLeaves 重载;或者你想知道SpitLeaves是否可以调用。
    • @iavr 如果SpitLeaves 有另一个重载,那么我可能会考虑标签调度。如果你想知道是否可以调用SpitLeaves,那么可以提供一个特征。我承认在某些情况下 SFINAE 可能是最佳选择。
    猜你喜欢
    • 2021-12-31
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2011-07-26
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多