广义外积 - 函数接受 lambda

Generalized Outer Product - function accepts lambda

我正在尝试创建一个函数,该函数将以创建动态分配的二维数组(矩阵)的方式生成两个数组的广义外积。 前两个参数的类型必须与后两个参数的类型相同,但前两个参数的类型不必与后两个参数的类型相同(例如前两个参数可以是指针到整数组)。这也意味着这两个数组的元素不一定必须是同一类型。至于 function ,它应该是一个 lambda 函数。唯一的限制是它必须能够接收这两个数组的元素作为参数。函数返回值的类型可以是任意类型,该类型将是矩阵的元素。

例子

5 2 8

1 3 6 2

函数 f(x,y)=x+2y

7 11 17 9

4 8 14 6

10 14 20 12

#include <iostream>
#include <vector>

/*I don't know how to make Generalized_Outer_Product accept lambda function*/
template < typename iterator_tip1, typename iterator_tip2, typename tip >
  auto Generalized_Outer_Product(iterator_tip1 start_first, iterator_tip1 after_end_first,
    iterator_tip2 start_second, iterator_tip2 after_end_second, tip f(tip)) {
    using type_of_object_first = typename std::decay < decltype( * start_first) > ::type;
    using type_of_object_second = typename std::decay < decltype( * start_second) > ::type;
    int n1 = std::distance(start_first, after_end_first);
    int n2 = std::distance(start_second, after_end_second);
    type_of_object_first ** mat = nullptr;
    mat = new int * [n1];
    for (int i = 0; i < n1; i++)
      mat[i] = nullptr;
    try {
      for (int i = 0; i < n1; i++)
        mat[i] = new type_of_object_first[n2];

      for (int i = 0; i < n1; i++) {
        for (int j = 0; j < n2; j++) {
          mat[i][j] = f( * start_first, * start_second);
          start_second++;
        }
        start_first++;
        start_second -= n2;
      }
    } catch (...) {
      for (int i = 0; i < n1; i++)
        delete[] mat[i];
      delete[] mat;
      throw std::range_error("Not enough memory");
    }
    return mat;
  }

int main() {
  int n1, n2, x;
  std::cin >> n1;
  std::vector < int > a, b, c;
  for (int i = 0; i < n1; i++) {
    std::cin >> x;
    a.push_back(x);
  }
  std::cin >> n2;
  for (int i = 0; i < n2; i++) {
    std::cin >> x;
    b.push_back(x);
  }
  try {
    std::cout << "Generalized Outer Product f(x,y)=x+2y:" << std::endl;
    int ** mat = nullptr;
    mat = Generalized_Outer_Product(a.begin(), a.end(), b.begin(), b.end(), [](int x, int y) {
      return x + 2 * y;
    });
    for (int i = 0; i < n1; i++) {
      for (int j = 0; j < n2; j++)
        std::cout << mat[i][j] << " ";
      std::cout << std::endl;
    }
    for (int i = 0; i < n1; i++)
      delete[] mat[i];
    delete[] mat;
  } catch (std::range_error e) {
    std::cout << e.what();
  }

  return 0;
}

你能帮我正确接受lambda函数吗?

TL DR

只保留参数类型开放,让编译器处理无效的参数类型;使用decltype判断结果元素类型:

template<class Iterator1, class Iterator2, class Function>
auto GeneralizedOuterProduct(Iterator1 const start1, Iterator1 const end1, Iterator2 const start2, Iterator2 const end2, Function&& f)
{
    using ResultType = decltype(f(*start1, *start2));
    ...
}

其他建议 + 完整代码

首先为自己创建一个管理数据生命周期的类型。这简化了异常处理。如果操作正确,您可以简单地在 GeneralizedProduct 函数中不处理您的异常,而不必担心内存泄漏。

下面的class只是简单的用一个std::vector来存储一行一行的数据。索引运算符利用向量迭代器允许我们编写 iter[index] 来获取对迭代器位置之后的元素 index 元素的引用。

template<class T>
struct MultiplicationResult
{
public:
    using ValueType = T;
    using IndexOperatorElement = std::vector<ValueType>::iterator;
    using IndexOperatorElementConst = std::vector<ValueType>::const_iterator;

    MultiplicationResult(size_t dimension1, std::vector<T>&& data)
        : m_dimension1(dimension1)
    {
        if (data.size() % dimension1 != 0)
        {
            throw std::runtime_error("invalid data: the number of data elements is not divisible by dimension1");
        }
        m_dimension2 = data.size() / dimension1;
        m_data = std::move(data);
    }

    IndexOperatorElement operator[](size_t index)
    {
        return m_data.begin() + index * m_dimension2;
    }
    
    IndexOperatorElementConst operator[](size_t index) const noexcept
    {
        return m_data.cbegin() + index * m_dimension2;
    }

    size_t Dimension1() const noexcept
    {
        return m_dimension1;
    }

    size_t Dimension2() const noexcept
    {
        return m_dimension2;
    }
private:
    std::vector<T> m_data;
    size_t m_dimension1;
    size_t m_dimension2;
};

现在 GeneralizedProduct 函数要做的就是确定结果类型并创建将用作 MultiplicationResult.

数据的向量

请注意,我们不以任何方式限制此处传递的函数的类型。如果给定取消引用的迭代器无法调用传入参数的调用运算符,或者如果结果类型不是可以用作 std::vector 的模板参数的类型,则编译将导致错误。请注意,lambda 是具有调用运算符的对象,因此不能分配给 result_type(argument_type1, argument_type2)。 (我认为这种方法比使用 std::function<result_type(argument_type1, argument_type2)>:

更方便
template<class Iterator1, class Iterator2, class Function>
auto GeneralizedOuterProduct(Iterator1 const start1, Iterator1 const end1, Iterator2 const start2, Iterator2 const end2, Function&& f)

我们简单地使用 decltype:

来确定调用调用运算符的结果类型
using ResultType = decltype(f(*start1, *start2));

template<class Iterator1, class Iterator2, class Function>
auto GeneralizedOuterProduct(Iterator1 const start1, Iterator1 const end1, Iterator2 const start2, Iterator2 const end2, Function&& f)
{
    using ResultType = decltype(f(*start1, *start2));

    size_t const n1 = std::distance(start1, end1);

    // if an exception happens, data will ensure the elements already allocated get destroyed
    std::vector<ResultType> data;
    data.reserve(n1 * std::distance(start2, end2));

    for (Iterator1 pos1 = start1; pos1 != end1; ++pos1)
    {
        for (Iterator2 pos2 = start2; pos2 != end2; ++pos2)
        {
            data.push_back(f(*pos1, *pos2));
        }
    }

    return MultiplicationResult(n1, std::move(data)); // C++17 CTAD deduces the template arguments here
}

示例用法(无异常处理:

int main() {
    std::vector<int> a{ 5, 2, 8 };
    std::vector<int> b{ 1, 3, 6, 2 };

    auto result = GeneralizedOuterProduct(a.begin(), a.end(), b.begin(), b.end(), [](int x, int y) { return x  + 2* y; });

    size_t const d1 = result.Dimension1();
    size_t const d2 = result.Dimension2();

    for (size_t i = 0; i != d1; ++i)
    {
        auto innerArray = result[i];
        for (size_t j = 0; j != d2; ++j)
        {
            std::cout << std::setw(5) << innerArray[j];
        }
        std::cout << '\n';
    }
}