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

Implicit conversion in template type deduction

本文关键字:转换 类型      更新时间:2023-10-16

在这个问题中,OP询问为什么他的std::accumulate函数返回错误的值。答案是因为他传入了初始值的int,因此计算全部int s而不是 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?因为隐式转换?

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;
}

直接问题的答案已经说明:如果可以从参数列表中推断出类型,则将其推导出来。将使用的类型设置为嵌套类型可防止推导它:

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';