从模板化容器类型中获取元素类型

时间:2014-05-12 21:09:22

标签: c++ templates stl

我正在尝试编写一个模板函数,该函数采用任意数字类型的任意容器类:

template <typename NumType, typename ContType>
double avg_nums(const ContType<NumType> & data)
{
    double sum = 0;

    for(ContType::const_iterator iter = data.cbegin(); iter != data.cend(); ++iter)
    {
        if(typeid(NumType) == typeid(char) || typeid(NumType) == typeid(unsigned char))
        {
            std::cout << static_cast<int>(*iter) << std::endl;
        }
        else
        {
            std::cout << *iter << " ";
        }

        sum += *iter;
    }
    std::cout << std::endl;

    return sum  / data.size();
}

但这会产生语法错误(VS2012)。这也不起作用:

template <typename NumType, typename ContType<NumType> >
double avg_nums(ContType<NumType> & data)

我希望容器中的对象类型是模板参数以及容器类型,因此我可以添加对NumType的特定情况的检查。如果我将功能签名更改为

template <typename NumType, typename ContType>
double avg_nums(ContType & data)

它构建但现在它依赖于显式指定正确的模板参数:

std::vector<char> cvec = boost::assign::list_of(0) (1) (1) (2) (3) (5);
std::vector<int> ivec = boost::assign::list_of(0) (1) (1) (2) (3) (5);

avg_nums<char, std::vector<char>>(cvec);
avg_nums<int, std::vector<int>>(ivec);

如果我将签名更改为

template <typename ContType>
double avg_nums(ContType & data)

它可以工作,但现在我没有直接访问元素类型。我可以从容器类型(std::vector<int>std::list<unsigned char>)获取元素类型吗?

4 个答案:

答案 0 :(得分:4)

您可以使用此表单:

template <typename ContType>
double avg_nums(ContType & data)

然后将元素类型设为

typename ContType::value_type

答案 1 :(得分:1)

template <typename ContType>
double avg_nums(ContType & data)
  

虽然有效,但现在我无法直接访问元素类型。我可以从容器的类型(std :: vector或std :: list)中获取元素类型吗?

您可以通过嵌套类型提取标准容器中包含元素的类型:value_type

typedef typename ContType::value_type NumType;

我不认为我会实现该功能,因为您可以使用经过充分测试的现有算法:

int value = 0;
int total = std::accumulate(container.begin(), container.end(), value);
double avg = total * 1. / container.size();

答案 2 :(得分:1)

如果容器类型没有某种'value_type'(这是首选方式,因为这个依赖于值类型是第一个不是第一个模板参数),下面的模板特化变体也可能会很有趣必然如此):

    // define some custom container where we do not have some kind of 'value_type'
    template <typename... T> class CustomContainer {};

    // forward declare the template function so we can deduce the type in a specialization
    template <typename ContType> double avg_nums(ContType&);

    // specialization that deduces ContType and NumType from a single template argument
    // This works for all continers where the first template argument is the 'value_type'
    // also note the Ts... which is a placeholder for possible other template arguments that need to be passed on
    template <template <typename...> class ContType, typename NumType, typename... Ts>
    double avg_nums(ContType<NumType, Ts...>&)
    {
        // so the work
        return NumType(0);
    }

    int main(int,char**)
    {
        CustomContainer<int> cont;
        avg_nums(cont);
        return 0;
    }

答案 3 :(得分:0)

针对您当前遇到的特定问题,您可以替换

template <typename NumType, typename ContType>
double avg_nums(const ContType<NumType> & data)
{

template< class ContType >
double avg_nums(ContType const& data)
{
    using NumType = typename ContType::value_type;

循环头中的类型规范需要typename


更一般地说,你可以做到

template< template< class, class > class ContType_, class NumType, class Allocator >
double avg_nums(ContType_<NumType, Allocator> const& data)
{
    using ContType = ContType_<NumType, Allocator>;

而不是

    if(typeid(NumType) == typeid(char) || typeid(NumType) == typeid(unsigned char))
    {
        std::cout << static_cast<int>(*iter) << std::endl;
    }
    else
    {
        std::cout << *iter << " ";
    }

您可以使用隐式促销并撰写

    std::cout << +*iter << " ";

或者0+*iter如果你发现更清楚的话。


顺便说一句,请考虑使用std::accumulate


另外,顺便提一下,请考虑如何处理空容器。


最后,您可以使用基于C ++ 11范围的for代替显式迭代器用法。