【问题标题】:Is There a Shortcut to decltype是否有 decltype 的快捷方式
【发布时间】:2016-03-28 18:05:39
【问题描述】:

this answer我写了C++17代码:

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

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

decay_t&lt;decltype(numbers[0])&gt;{} 是一种非常复杂的获取方式:

numbers元素的零初始化类型

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

编辑:

我有很多答案涉及accumulate 或从numbers[0] 中提取类型的包装器。问题是他们需要读者导航到一个次要位置来阅读一个不比初始化代码decay_t&lt;decltype(numbers[0])&gt;{}复杂的解决方案。

我们必须做更多的唯一原因:decltype(numbers[0]) 是因为 array subscript operator 返回一个引用:

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

有趣的是关于decltype的论点:

如果一个对象的名字被括号括起来,它被视为一个普通的左值表达式

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

【问题讨论】:

  • 如果你想要一个零,就写成这样:0。不需要在Ossa上堆Pelion。
  • @n.m.先生,这是绝对错误的。 0accumulateinit 定义为 int。因此,例如,如果我们正在使用float numbers[],则这些值将被截断。例如,This example 应该输出 11.2,但如果我使用 0,它将输出 10。
  • 是的,你是对的。我忘了 std::accumulate 坏了...
  • @n.m.呵呵,我当然不会说那是坏的,而是非常有用。我经常使用accumulate 在lambda 中使用to_string 从数字向量构造字符串。顺便说一句,如果类型不匹配,Visual Studio 2015 会触发 C4244 warning。因此,对于警告级别升高并正在关注它们的人,他们不会犯此错误。
  • 好吧,一个让你写像decay_t(decltype(..)) 这样的东西的 API 坏了。正常人应该不需要知道这些。在 C++11 中,我们可以将其封装在累加器中,但到目前为止,我们仍坚持使用 '03 API。

标签: c++ c++11 types decltype associated-types


【解决方案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&lt;It&gt; 使用额外的间接级别,并通过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_beginbegin() 的一个版本,它负责ADL。

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

      【讨论】:

      • 嗯...什么是 ADL?
      • @JonathanMee 参数相关查找
      • 你能帮我理解beginendADL有什么关系吗?这只是为了绕过 C++14 的要求吗?
      • @JonathanMee 如果你把这些词放在一起用谷歌搜索,你会发现几个非常相关的问题。
      • 所以我可以在ADL in relationship to begin/end 上找到的最佳资源说:“对于仅使用标准容器或 C 样式数组的代码,std::begin() 和 std::end() 可能是在没有引入 using-declarations 的情况下到处调用”所以你只是写 adl_begin/adl_end 因为我没有限定 numbers 是“标准容器或 C 样式数组”?
      猜你喜欢
      • 2015-10-04
      • 1970-01-01
      • 1970-01-01
      • 2021-09-10
      • 1970-01-01
      • 1970-01-01
      • 2011-06-23
      • 1970-01-01
      相关资源
      最近更新 更多