是否有 decltype 的快捷方式(Is There a Shortcut to decltype)

问题

在这个答案中,我编写了 C++17 代码:

cout << accumulate(cbegin(numbers), cend(numbers), decay_t<decltype(numbers[0])>{});

这收到了一些关于 C++ 类型关联性质的负面评论,我很遗憾地说我同意:(

decay_t<decltype(numbers[0])>{}是一种非常复杂的获取方法:

numbers元素的零初始化类型

是否可以保持与numbers元素类型的关联,但不能键入 30 个字符来获取它?

编辑:

我有很多答案涉及用于accumulate或从numbers[0]中提取类型的包装器。 问题是它们需要读者导航到辅助位置以阅读不比初始化代码decay_t<decltype(numbers[0])>{}复杂的解决方案。

我们必须做更多的唯一原因: decltype(numbers[0])是因为数组下标运算符返回一个引用:

错误:将“int”类型的右值表达式无效转换为“int&”类型

有趣的是,关于 decltype 的论点:

如果对象的名称带括号,则将其视为普通的左值表达式

但是, decltype((numbers[0]))仍然只是对numbers元素的引用。 所以最后这些答案可能与我们可以简化这个初始化一样接近:(

回答1

虽然我总是会选择按照@Barry 编写辅助函数,但如果 numbers 是标准容器,它将导出 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));
回答2

个人偏好:我发现decay_tdecltypedeclval舞蹈非常烦人且难以阅读。

相反,我会通过类型特征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 */ }
回答3

我认为你能做的最好的就是在某处考虑这一点:

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是考虑 ADL 的begin()版本。

当然,从技术上讲,我们仍然拥有您之前试图避免的所有垃圾……但至少现在您不必再看一遍了?


更多相关内容:请点击查看