【问题标题】:c++: why template cannot be used to deduce both container and element type?c++:为什么不能使用模板来推断容器和元素类型?
【发布时间】:2017-06-20 14:00:58
【问题描述】:

我有一个非常简单的测试程序,如下所示:

#include<vector>
#include<iostream>
using namespace std;
template<typename C, typename E>
void f(const C<E>& container){
    cout<<container.size()<<endl;
}
int main(){
    vector<int> i;
    f(i);
    return 0;
}

使用 gcc 4.1.2 编译失败。错误信息是:

templateContainer.cpp:5: error: ‘C’ is not a template
templateContainer.cpp: In function ‘int main()’:
templateContainer.cpp:10: error: no matching function for call to ‘f(std::vector<int, std::allocator<int> >&)’

【问题讨论】:

  • 这就是容器具有关联类型的原因。你不需要E;只需写typename C::value_type(或typename C::reference_type,视情况而定)。
  • 澄清一下,错误消息是因为 C定义为类型的名称 (typename C),但它被使用作为模板的名称 (C&lt;E&gt;)。不能两者兼有。

标签: c++ class templates containers typename


【解决方案1】:

您可以使用模板模板参数(注意std::vector 实际上需要多个模板参数[一个元素类型和一个分配器类型])。:

template<template <typename...> class C, typename... E>
void f(const C<E...>& container){
    cout<<container.size()<<endl;
}

Live Demo


如果你不需要类型分解,你可以简单地使用一个普通的模板。

template<typename C>
void f(const C& container){
    cout<<container.size()<<endl;
}

您还可以从 STL 容器中获取 typedef:例如,如果您想知道容器中包含的元素的 typevalue_type 就在那里。

template<typename C>
void f(const C& container){
    using ValueType = typename C::value_type;
    cout<<container.size()<<endl;
}

【讨论】:

  • 没有可变参数模板(即C++11)需要提前知道参数的数量吗?
  • @tobi303,是的,否则,使用许多模板参数重载的一些旧的令人不快的技巧
  • 请注意,虽然这绝对是对上述问题的正确答案,但如果你真的用它来推断容器的值类型,你应该当场被解雇。容器具有关联类型的 typedef,您应该始终使用它们。因为容器契约需要这些并且根本不要求值类型是模板参数。
【解决方案2】:

std::vector 有两个模板参数,类型和分配器。

template <template<class, class> class C, class E, class A>
void f(const C<E, A> &container) 
{
   std::cout << container.size() << endl;
}

int main()
{
   std::vector<int> i;
   f(i);
   return 0;
}

【讨论】:

    【解决方案3】:

    虽然 WhiZTiM 的回答是正确的(嗯,更喜欢第二部分),但它并没有解释为什么您的代码不起作用。

    暂时假设你大致打算

    template<template <typename> class C, typename E> void f(const C<E>&);
    

    std::vector 不匹配的原因是它的形状错误 - 它有两个类型参数,而不是您声明中的一个。

    仅仅因为您不经常显式编写默认的第二个(分配器)参数,并不意味着它不存在。

    为了比较,这以类似的方式有效(或无效):

    void f(int);
    void g(int, int* = nullptr);
    
    void apply(void (*func)(int), int);
    
    apply(f, 42); // ok - f matches shape void(*)(int)
    apply(g, 42); // error - g is really void(*)(int,int*)
    

    具体来说,默认参数(或类型参数)是语法糖。它们允许您忘记调用(实例化)站点上的那些参数,但不会更改函数(或模板)的形状。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2011-12-31
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2012-03-26
      相关资源
      最近更新 更多