模板类型推导中的隐式转换

时间:2013-12-23 02:24:47

标签: c++ templates iterator type-conversion

在这个question中,OP询问他的std::accumulate函数返回错误值的原因。答案是因为他传递int的初始值,因此计算所有int而不是double s。我有两个版本:

template <class InputIterator, class T>
auto accumulate (InputIterator first, InputIterator last, T init)
{
  typedef typename std::iterator_traits<InputIterator>::value_type vt;
  vt init2 = vt(init);
  // ...
  return init2;
}

template <class InputIterator, class T = typename std::iterator_traits<InputIterator>::value_type>
T not_working (InputIterator first, InputIterator last, T init)
{
// ...
return init;
}

为什么版本2 T仍为int?因为隐式转换?

2 个答案:

答案 0 :(得分:3)

T仍然被推断,覆盖了默认参数。 :)

你想要这个:

template <class InputIterator>
typename std::iterator_traits<InputIterator>::value_type
working(InputIterator first, InputIterator last,
        typename std::iterator_traits<InputIterator>::value_type init) {
    return init;
}

答案 1 :(得分:1)

已经说明了当前问题的答案:如果类型可以从参数列表中推断出来,那么它将被推导出来。使用的类型是嵌套类型可以防止它被推断出来:

template <typename T>
struct identity {
    typedef T type;
};
template <typename It, typename T = typename std::iterator_traits<It>::value_type>
T safe_accumulate(It begin, It end, typename identity<T>::type sum) {
    return std::accumulate(begin, end, sum);
}

但是,在某些情况下,需要指定算法的结果类型。例如,如果要在冗长的字符串中对char值求和。使用上面定义的safe_accumulate()会使结果类型指定有点痛苦:

std::string s("hello, world");
std::cout << safe_accumulate<std::string::iterator, long>(s.begin(), s.end(), 0) << '\n';

可以通过将结果类型放在第一位的方式完成实现:

template <typename T = void, typename It>
typename std::conditional<std::is_same<T, void>::value,
    typename std::iterator_traits<It>::value_type,
    T>::type
safe_accumulate(It begin, It end,
                typename std::conditional<std::is_same<T, void>::value,
                    typename std::iterator_traits<It>::value_type,
                    T>::type sum) {
    return std::accumulate(begin, end, sum);
}

现在算法可以更方便的方式使用,即算法的实现者为了用户的利益提供了一些更复杂的实现:

std::cout << safe_accumulate<long>(s.begin(), s.end(), 0) << '\n';