我正在尝试创建一个函数,该函数将以创建动态分配的2D数组(矩阵)的方式使两个数组的广义外积。前两个形参的类型必须与后两个形参的类型相同,但前两个形参的类型不必与后两个形参的类型相同(例如,前两个形参可以是指向整型数组的指针)。这也意味着这两个数组的元素不必是相同的类型。至于函数𝑓,它应该是一个lambda函数。唯一的限制是它必须能够接收这两个数组的元素作为参数。函数𝑓返回的值的类型可以是任意的,该类型将是矩阵的元素。
例子5 2 8
1 3 6 2
函数f (x, y) = x + 2 y
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
函数中不被处理,而不必担心内存泄漏。
下面的类简单地使用一个std::vector
来存储一行接一行的数据。索引操作符利用了这样一个事实,即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';
}
}