将指向数据的指针作为参数传递给期望二维数组的函数

5
考虑以下代码:
#include<algorithm>
#include<iostream>
#include<array>

void show(double x[2][2]) {
  std::cout<<x[0][0]<<", "<<x[0][1]<<std::endl
           <<x[1][0]<<", "<<x[1][1]<<std::endl;
}

int main() {
  std::array<double, 4> y = {1, 2, 3, 4};  
  double x[2][2];

  // it is safe to copy because x[2][2] consists of
  // four contiguous blocks of memory in row-major order

  std::copy(y.begin(), y.end(), &x[0][0]);

  show(x); // this, obviously, works as expected

  // but how can I cast y, or y.data(),
  // or y.begin() to use the function foo?    
  // show(y); 
}

我正在使用一个遗留库,其中许多函数参数的格式类似于 x[a][b]。然而,我的代码依赖于线性数据表示(也就是说,我只使用C++“线性”容器,例如 std::array<T, N>)。
想象一下,在艰苦的计算之后,我的代码已经到达了一个点,std::array<double, 2> 包含了我需要的数据,现在我需要在该数据上调用 foo
如何“转换”底层容器,以便我可以调用期望一个 double[2][2] 的遗留函数?
我真的不想拷贝(如示例中所示),因为像 foo 这样的遗留函数会被调用数十万次。
作为极大的优势,我希望将这些遗留函数包装在类似于 C++ 算法界面的背后;类似于以下方式:
std::vector<std::array<double, 4>> z;
fooify(z.begin(), z.end()); // calls foo(zi) for each zi in z

编辑:一些答案

感谢 @6502,我开始采用以下解决方案:

#include<algorithm>
#include<iostream>
#include<array>

namespace legacy {
void show(double x[2][2]) {
  std::cout<<x[0][0]<<", "<<x[0][1]<<std::endl
           <<x[1][0]<<", "<<x[1][1]<<std::endl;
}
}

template<size_t N, typename Container>
void show(Container& y) {
  return legacy::show(reinterpret_cast<double(*)[N]>(y.data()));
}

int main() {
  std::array<double, 4> y = {1, 2, 3, 4};  
  show<2>(y);
}

这个函数的功能与预期相符 - 当然,我可以自动推导出“重新塑形”的因素(在本例中为2,但在一般情况下会有所变化)。

接下来,我将尝试将这个“重构”函数纳入算法中。

为了完整起见,我附加编译细节(使用GCC 4.8.1的OS X 10.7.4):

$ g++ example.cpp -std=c++11 -Wall -Wextra
$ ./a.out                                                 
1, 2
3, 4

f(T a[2][2]) 实际上是 f(T (* a)[2]) - 你应该使用函数签名来明确表示这一点,而不是完全误导性的语法 a[2][2](实际上应该被弃用,因为它对程序员来说是在撒谎)。这表明这些类型实际上是不兼容的。 - Konrad Rudolph
我理解这个问题,但是为了澄清一下,您想要将一个包含两个元素的线性数组传递给一个实际上期望四个元素的函数(数据合并只是一个副产品,但这非常重要)。 - WhozCraig
1
@WhozCraig 反过来,肯定是这样的吧?show 函数期望一个包含两个指针的数组,而 OP 正试图传递一个包含四个 double 的数组。 - Konrad Rudolph
@KonradRudolph show() 函数期望一个指针,而不是两个指针的数组。这是一个真正的 2D 数组。OP 更改了 std::array<> 的声明以解决从 24 的空间差异问题,因此回答了我的问题,很可能只是他的笔误。 - WhozCraig
@WhozCraig 嗯,我有点傻。请不要理我。 - Konrad Rudolph
显示剩余3条评论
1个回答

3

使用 C 风格的类型转换

show((double (*)[2])y.data());

或者,如果你愿意打更多的字,可以使用 reinterpret_cast。
show(reinterpret_cast<double (*)[2]>(y.data()));

2
我认为这两个不兼容布局。这个转换将失败。 - Konrad Rudolph
我不会使用C风格的转换,而且reinterpret_cast<>是一个相当大的工具,很少需要用于这样的情况。 - WhozCraig
只是为了澄清,我的断言“不起作用”实际上是基于一个测试 - 不幸的是,我没有删除之前传递nullptr到函数的测试。所以一切都好。这确实有效,并且正如WhozCraig在下面的评论中解释的那样,布局不兼容实际上在这里不是问题。这仍然可能是UB(reinterpret_cast的规范很难阅读,但我找不到任何允许此转换的内容),但它在GCC上工作,并且可能在每个理智的编译器上都可以。 - Konrad Rudolph
2
@WhozCraig:在我看来,这是使用reinterpret_cast(或C风格转换)的完美情况。从哲学角度来看,2x2数组与4元素数组完全无关,但在内存中的布局仍然相同,这是需要告诉编译器忽略类型并信任程序员的情况。 - 6502
@6502所言的是,一个格式正确的static_cast<>不起作用吗? - WhozCraig
显示剩余8条评论

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接