【问题标题】:Using auto to Return Different Lambdas使用 auto 返回不同的 Lambda
【发布时间】:2017-11-03 13:30:14
【问题描述】:

所以我很好奇 是否让我能够灵活地执行此操作。我有this answer,其中包含代码:

template <typename T>
function<void(vector<pair<T, T>>&)> vertex_triangle(const size_t index, const vector<pair<T, T>>& polygon) {
    if (0U == index){
        return [&](vector<pair<T, T>>& output){ output.push_back(polygon.back());
                                                output.push_back(polygon.front());
                                                output.push_back(polygon[1U]); };
    }else if (index == (polygon.size() - 1U)){
        return [&](vector<pair<T, T>>& output){ output.push_back(polygon[polygon.size() - 2U]);
                                                output.push_back(polygon.back());
                                                output.push_back(polygon.front()); };
    }else{
        return [&](vector<pair<T, T>>& output){ output.push_back(polygon[index - 1U]);
                                                output.push_back(polygon[index]);
                                                output.push_back(polygon[index + 1U]); };
    }
}

我认为我应该能够将函数签名更改为:auto vertex_triangle(const size_t index, const vector&lt;pair&lt;T, T&gt;&gt;&amp; polygon),从而保留闭包类型优化。此外,我真的很想让我的 lambda 参数为 auto&amp; 而不是 vector&lt;pair&lt;T, T&gt;&gt;&amp;
会支持这些更改吗?

【问题讨论】:

  • lambda 有不同的类型,因此auto 将无法推断出返回类型。如果 indexconstexpr 您可以使用 if constexpr 并在编译时做出决定。
  • 对于auto vertex_triangle(const size_t index, const vector&lt;pair&lt;T, T&gt;&gt;&amp; polygon),您想为auto 推导出什么?每个 lambda 都有不同的类型,并且由于它们捕获,它们也没有共同的类型。

标签: c++17 c++17 c++ lambda closures c++17 auto


【解决方案1】:

会支持这些更改吗?

没有。您要求的是有一个根据运行时条件返回不同类型的函数。做到这一点的唯一方法是类型擦除 - 无论是 std::function 类型还是标准 OOP 继承类型。

如果您可以将条件提升为常量表达式,那么您可以使用if constexpr(或简单地标记调度)来完成此操作。但鉴于第二种情况是index == vector.size() - 1,我猜这是不可能的。


也就是说,你真的需要不同的功能吗?为什么不呢(注意:通过复制捕获 index 以避免悬空引用):

template <typename T>
auto vertex_triangle(const size_t index, const vector<pair<T, T>>& polygon) {
    return [&polygon, index](vector<pair<T, T>>& output){ 
        size_t lo = index == 0 ? polygon.size() - 1 : index - 1;
        size_t hi = index == polygon.size() - 1 ? 0 : index + 1;

        for (size_t offset : {lo, index, hi}) {       
            output.push_back(polygon[offset]);
        }
    };
}

【讨论】:

  • 可能:size_t lo = (index + polygon.size() - 1) % polygon.size();size_t hi = (index + 1) % polygon.size();
  • @Jarod42 这样更直接。
【解决方案2】:

不,函数的返回类型不能因非模板值函数参数而异。

你可以这样写一个变体工厂:

template<class F, class T, T t, T...ts>
auto variant_from(
  F&& f,
  std::integral_constant<T, t> which,
  std::integer_sequence<T, ts...>
) ->
std::variant< std::decay_t<
  std::result_of_t< F&&( std::integral_constant<T, t>)>
>... > {
  return std::forward<F>(f)( which );
}

使用它,我们可以返回 lambdas 上的变体,它与您所能得到的一样接近。

template <typename T>
auto vertex_triangle(const size_t index, const vector<pair<T, T>>& polygon) {
  auto choice_list = std::make_index_sequence<3>{};
  auto algorithm = [&polygon, index](auto choice) {
    if constexpr (choice==0){
      return [&polygon](vector<pair<T, T>>& output){ 
        output.push_back(polygon.back());
        output.push_back(polygon.front());    
        output.push_back(polygon[1U]);
      };
    }else if constexpr (choice == 1){
      return [&polygon](vector<pair<T, T>>& output){
        output.push_back(polygon[polygon.size() - 2U]);
        output.push_back(polygon.back());
        output.push_back(polygon.front());
      };
    }else{
      return [&polygon, index](vector<pair<T, T>>& output){
        output.push_back(polygon[index - 1U]);
        output.push_back(polygon[index]);
        output.push_back(polygon[index + 1U]); };
      }
    };
  if (index == 0)
    return variant_from( algorithm, std::integral_constant<std::size_t, 0>{}, choice_list );
  else if (index == output.size()-1)
    return variant_from( algorithm, std::integral_constant<std::size_t, 1>{}, choice_list );
  else
    return variant_from( algorithm, std::integral_constant<std::size_t, 2>{}, choice_list );
}

我相信这可以清理一下。

variant_from 使用算法来推断变量是什么类型的和,然后只将其中一个存储在返回的变量中。我们在 3 个不同的上下文中调用它,它们具有相同的返回类型,但存储的值不同。

请注意,variant 上的 operator() 并不能满足我们的要求,但我们可以对其进行扩充。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2016-12-01
    • 2019-02-04
    • 1970-01-01
    • 2020-02-12
    • 1970-01-01
    • 1970-01-01
    • 2020-04-22
    • 1970-01-01
    相关资源
    最近更新 更多