在this answer中,我编写了C ++ 17代码:
cout << accumulate(cbegin(numbers), cend(numbers), decay_t<decltype(numbers[0])>{});
此接收到关于C ++&#39的性质一些负面评论; S类型关联,这是我&#39; M悲伤地说,我同意:(
decay_t<decltype(numbers[0])>{}
是一种非常复杂的获取方式:
元素的零初始化类型
numbers
是否可以保持与numbers
&#39;的类型的关联?得到它的元素,但不是30个字符的类型?
修改
我有很多答案涉及accumulate
的包装器或从numbers[0]
中提取类型。问题是他们需要读者导航到辅助位置以读取与初始化代码decay_t<decltype(numbers[0])>{}
同样复杂的解决方案。
我们必须做的更多的唯一原因是:decltype(numbers[0])
是因为array subscript operator返回了引用:
错误:类型&#39; int&#39;的右值表达式的无效转换输入&#39; int&amp;&#39;
关于decltype
的论点,这很有意思:
如果对象的名称带括号,则将其视为普通左值表达式
但是,decltype((numbers[0]))
仍然只是对numbers
元素的引用。所以最后这些答案可能就像我们简化这种初始化一样接近:(
答案 0 :(得分:2)
虽然我总是选择按照@Barry编写辅助函数, 如果数字是标准容器,它将导出类型value_type,因此您可以节省一点复杂性:
cout << accumulate(cbegin(numbers), cend(numbers), decltype(numbers)::value_type());
更进一步,我们可以定义这个模板函数:
template<class Container, class ElementType = typename Container::value_type>
constexpr auto element_of(const Container&, ElementType v = 0)
{
return v;
}
给了我们这个:
cout << accumulate(cbegin(numbers), cend(numbers), element_of(numbers, 0));
答案 1 :(得分:2)
个人偏好:我发现decay_t
,decltype
和declval
舞蹈非常烦人且难以阅读。
相反,我会通过类型特征value_t<It>
使用额外的间接级别,并通过init = R{}
进行零初始化
template<class It>
using value_t = typename std::iterator_traits<It>::value_type;
template<class It, class R = value_t<It>>
auto accumulate(It first, It last, R init = R{}) { /* as before */ }
答案 2 :(得分:1)
我认为你能做的最好的事情就是把它解决到某个地方:
template <class It, class R = std::decay_t<decltype(*std::declval<It>())>>
R accumulate(It first, It last, R init = 0) {
return std::accumulate(first, last, init);
}
std::cout << accumulate(cbegin(numbers), cend(numbers));
或更一般地说:
template <class Range, class T =
std::decay_t<decltype(*adl_begin(std::declval<Range&&>()))>>
T accumulate(Range&& range, T init = 0) {
return std::accumulate(adl_begin(range), adl_end(range), init);
}
cout << accumulate(numbers);
其中adl_begin
是begin()
的一个版本,用于说明ADL。
当然,我们在技术上仍然拥有你之前想要避免的所有瑕疵......但至少现在你再也不用看它了?