使用auto返回不同的Lambda

时间:2017-11-03 13:30:14

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

所以我很好奇是否给了我这样做的灵活性。我有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<pair<T, T>>& polygon),从而保留闭包类型优化。另外,我真的希望让我的lambda参数为auto&而不是vector<pair<T, T>>&
会支持这些更改吗?

2 个答案:

答案 0 :(得分:9)

  

会支持这些更改吗?

没有。您要求的是具有根据运行时条件返回不同类型的函数。唯一的方法是删除类型 - 无论是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]);
        }
    };
}

答案 1 :(得分:1)

不,函数的返回类型不能基于非模板值函数参数而变化。

你可以写一个像这样的变种工厂:

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个不同的上下文中调用它,它们具有相同的返回类型,但存储了不同的值。

请注意operator()上的variant并没有做我们想要的事情,但我们可以对其进行补充。