C++转换为较小尺寸的数组

发布于 2024-09-07 08:08:10 字数 1274 浏览 11 评论 0原文

这是一个关于 C++ 语言的各种怪癖的有趣问题。我有一对函数,它们应该用矩形的角填充点数组。它有两种重载:一种采用 Point[5],另一种采用 Point[4]。 5 点版本指的是闭合多边形,而 4 点版本指的是当您只需要 4 个角时。

显然这里有一些重复的工作,所以我希望能够使用 4 点版本来填充 5 点版本的前 4 个点,所以我不会重复该代码。 (并不是说要重复太多,但每当我复制和粘贴代码时,我都会产生严重的过敏反应,我想避免这种情况。)

问题是,C++ 似乎并不关心转换 T[m]T[n],其中 n 米。 static_cast 似乎认为这些类型由于某种原因不兼容。当然,reinterpret_cast 可以很好地处理它,但它是一种危险的动物,作为一般规则,最好尽可能避免。

所以我的问题是:是否有一种类型安全的方法将一个大小的数组转换为数组类型相同的较小大小的数组?

[编辑]代码,是的。我应该提到的是,参数实际上是对数组的引用,而不仅仅是指针,因此编译器知道类型差异。

void RectToPointArray(const degRect& rect, degPoint(&points)[4])
{
    points[0].lat = rect.nw.lat; points[0].lon = rect.nw.lon;
    points[1].lat = rect.nw.lat; points[1].lon = rect.se.lon;
    points[2].lat = rect.se.lat; points[2].lon = rect.se.lon;
    points[3].lat = rect.se.lat; points[3].lon = rect.nw.lon;
}
void RectToPointArray(const degRect& rect, degPoint(&points)[5])
{
    // I would like to use a more type-safe check here if possible:
    RectToPointArray(rect, reinterpret_cast<degPoint(&)[4]> (points));
    points[4].lat = rect.nw.lat; points[4].lon = rect.nw.lon;
}

[Edit2] 按引用传递数组的目的是让我们至少可以模糊地确定调用者正在传递正确的“输出参数”。

Here's an interesting question about the various quirks of the C++ language. I have a pair of functions, which are supposed to fill an array of points with the corners of a rectangle. There are two overloads for it: one takes a Point[5], the other takes a Point[4]. The 5-point version refers to a closed polygon, whereas the 4-point version is when you just want the 4 corners, period.

Obviously there's some duplication of work here, so I'd like to be able to use the 4-point version to populate the first 4 points of the 5-point version, so I'm not duplicating that code. (Not that it's much to duplicate, but I have terrible allergic reactions whenever I copy and paste code, and I'd like to avoid that.)

The thing is, C++ doesn't seem to care for the idea of converting a T[m] to a T[n] where n < m. static_cast seems to think the types are incompatible for some reason. reinterpret_cast handles it fine, of course, but is a dangerous animal that, as a general rule, is better to avoid if at all possible.

So my question is: is there a type-safe way of casting an array of one size to an array of a smaller size where the array type is the same?

[Edit] Code, yes. I should have mentioned that the parameter is actually a reference to an array, not simply a pointer, so the compiler is aware of the type difference.

void RectToPointArray(const degRect& rect, degPoint(&points)[4])
{
    points[0].lat = rect.nw.lat; points[0].lon = rect.nw.lon;
    points[1].lat = rect.nw.lat; points[1].lon = rect.se.lon;
    points[2].lat = rect.se.lat; points[2].lon = rect.se.lon;
    points[3].lat = rect.se.lat; points[3].lon = rect.nw.lon;
}
void RectToPointArray(const degRect& rect, degPoint(&points)[5])
{
    // I would like to use a more type-safe check here if possible:
    RectToPointArray(rect, reinterpret_cast<degPoint(&)[4]> (points));
    points[4].lat = rect.nw.lat; points[4].lon = rect.nw.lon;
}

[Edit2] The point of passing an array-by-reference is so that we can be at least vaguely sure that the caller is passing in a correct "out parameter".

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(6

葬シ愛 2024-09-14 08:08:10

我认为通过重载来做到这一点不是一个好主意。函数的名称不会告诉调用者它是否要填充打开的数组。如果调用者只有一个指针并且想要填充坐标(假设他想要填充多个矩形以成为不同偏移量的更大数组的一部分)怎么办?

我将通过两个函数来完成此操作,并让它们接受指针。大小不是指针类型的一部分

void fillOpenRect(degRect const& rect, degPoint *p) { 
  ... 
}

void fillClosedRect(degRect const& rect, degPoint *p) { 
  fillOpenRect(rect, p); p[4] = p[0]; 
}

我不明白这有什么问题。你的重新解释在实践中应该工作得很好(我不知道会出什么问题 - 对齐和表示都是正确的,所以我认为,仅仅形式上的不确定性不会在这里实现现实),但正如我所说上面我认为没有充分的理由让这些函数通过引用获取数组。


如果你想通用地做到这一点,你可以通过输出迭代器来编写它,

template<typename OutputIterator> 
OutputIterator fillOpenRect(degRect const& rect, OutputIterator out) { 
  typedef typename iterator_traits<OutputIterator>::value_type value_type;
  value_type pt[] = { 
    { rect.nw.lat, rect.nw.lon },
    { rect.nw.lat, rect.se.lon },
    { rect.se.lat, rect.se.lon },
    { rect.se.lat, rect.nw.lon }
  };
  for(int i = 0; i < 4; i++)
    *out++ = pt[i];
  return out;
}

template<typename OutputIterator>
OutputIterator fillClosedRect(degRect const& rect, OutputIterator out) { 
  typedef typename iterator_traits<OutputIterator>::value_type value_type;
  out = fillOpenRect(rect, out); 

  value_type p1 = { rect.nw.lat, rect.nw.lon };
  *out++ = p1;
  return out;
}

然后你可以将它与向量和数组一起使用,无论你最喜欢什么。

std::vector<degPoint> points;
fillClosedRect(someRect, std::back_inserter(points));

degPoint points[5];
fillClosedRect(someRect, points);

如果你想编写更安全的代码,你可以使用带有后插入器的向量方式,如果你使用较低级别的代码,你可以使用指针作为输出迭代器。

I don't think it's a good idea to do this by overloading. The name of the function doesn't tell the caller whether it's going to fill an open array or not. And what if the caller has only a pointer and wants to fill coordinates (let's say he wants to fill multiple rectangles to be part of a bigger array at different offsets)?

I would do this by two functions, and let them take pointers. The size isn't part of the pointer's type

void fillOpenRect(degRect const& rect, degPoint *p) { 
  ... 
}

void fillClosedRect(degRect const& rect, degPoint *p) { 
  fillOpenRect(rect, p); p[4] = p[0]; 
}

I don't see what's wrong with this. Your reinterpret-cast should work fine in practice (i don't see what could go wrong - both alignment and representation will be correct, so the merely formal undefinedness won't carry out to reality here, i think), but as i said above i think there's no good reason to make these functions take the arrays by reference.


If you want to do it generically, you can write it by output iterators

template<typename OutputIterator> 
OutputIterator fillOpenRect(degRect const& rect, OutputIterator out) { 
  typedef typename iterator_traits<OutputIterator>::value_type value_type;
  value_type pt[] = { 
    { rect.nw.lat, rect.nw.lon },
    { rect.nw.lat, rect.se.lon },
    { rect.se.lat, rect.se.lon },
    { rect.se.lat, rect.nw.lon }
  };
  for(int i = 0; i < 4; i++)
    *out++ = pt[i];
  return out;
}

template<typename OutputIterator>
OutputIterator fillClosedRect(degRect const& rect, OutputIterator out) { 
  typedef typename iterator_traits<OutputIterator>::value_type value_type;
  out = fillOpenRect(rect, out); 

  value_type p1 = { rect.nw.lat, rect.nw.lon };
  *out++ = p1;
  return out;
}

You can then use it with vectors and also with arrays, whatever you prefer most.

std::vector<degPoint> points;
fillClosedRect(someRect, std::back_inserter(points));

degPoint points[5];
fillClosedRect(someRect, points);

If you want to write safer code, you can use the vector way with back-inserters, and if you work with lower level code, you can use a pointer as output iterator.

一指流沙 2024-09-14 08:08:10

我会使用 std::vector(这真的很糟糕,不应该使用) 在某些极端情况下,你甚至可以通过像 Point 这样的指针使用普通数组* 然后你就不应该有这样的“转换”麻烦。

I would use std::vector or (this is really bad and should not be used) in some extreme cases you can even use plain arrays via pointer like Point* and then you shouldn't have such "casting" troubles.

_畞蕅 2024-09-14 08:08:10

为什么不直接传递一个标准指针,而不是一个大小的指针,就像这样

void RectToPointArray(const degRect& rect, degPoint * points ) ;

Why don't you just pass a standard pointer, instead of a sized one, like this

void RectToPointArray(const degRect& rect, degPoint * points ) ;
蓝眸 2024-09-14 08:08:10

我认为你对问题的框架/思考不正确。通常,您不需要具体键入具有 4 个顶点的对象与具有 5 个顶点的对象。

但如果您必须键入它,则可以使用 struct 来具体定义类型。

struct Coord
{
    float lat, long ;
} ;

然后

struct Rectangle
{
    Coord points[ 4 ] ;
} ;

struct Pentagon
{
    Coord points[ 5 ] ;
} ;

// 4 pt version
void RectToPointArray(const degRect& rect, const Rectangle& rectangle ) ;

// 5 pt version
void RectToPointArray(const degRect& rect, const Pentagon& pent ) ;

我认为这个解决方案有点极端,并且您可以使用 assert< 来检查其大小(为 4 或 5)。 /code>s,就可以了。

I don't think your framing/thinking of the problem is correct. You don't generally need to concretely type an object that has 4 vertices vs an object that has 5.

But if you MUST type it, then you can use structs to concretely define the types instead.

struct Coord
{
    float lat, long ;
} ;

Then

struct Rectangle
{
    Coord points[ 4 ] ;
} ;

struct Pentagon
{
    Coord points[ 5 ] ;
} ;

Then,

// 4 pt version
void RectToPointArray(const degRect& rect, const Rectangle& rectangle ) ;

// 5 pt version
void RectToPointArray(const degRect& rect, const Pentagon& pent ) ;

I think this solution is a bit extreme however, and a std::vector<Coord> that you check its size (to be either 4 or 5) as expected with asserts, would do just fine.

落墨 2024-09-14 08:08:10

我想您可以使用函数模板专门化,如下所示(简化示例,其中第一个参数被忽略,函数名称被 f() 等替换):

#include <iostream>
using namespace std;

class X
{
};

template<int sz, int n>
int f(X (&x)[sz])
{
    cout<<"process "<<n<<" entries in a "<<sz<<"-dimensional array"<<endl;
    int partial_result=f<sz,n-1>(x);
    cout<<"process last entry..."<<endl;

    return n;
}
//template specialization for sz=5 and n=4 (number of entries to process)
template<>
int f<5,4>(X (&x)[5])
{
    cout<<"process only the first "<<4<<" entries here..."<<endl;

    return 4;
}


int main(void)
{
    X u[5];

    int res=f<5,5>(u);
    return 0;
}

当然,您必须照顾其他(潜在危险)特殊情况,例如 n={0,1,2,3},您最好使用无符号整数而不是整数。

I guess you could use function template specialization, like this (simplified example where first argument was ignored and function name was replaced by f(), etc.):

#include <iostream>
using namespace std;

class X
{
};

template<int sz, int n>
int f(X (&x)[sz])
{
    cout<<"process "<<n<<" entries in a "<<sz<<"-dimensional array"<<endl;
    int partial_result=f<sz,n-1>(x);
    cout<<"process last entry..."<<endl;

    return n;
}
//template specialization for sz=5 and n=4 (number of entries to process)
template<>
int f<5,4>(X (&x)[5])
{
    cout<<"process only the first "<<4<<" entries here..."<<endl;

    return 4;
}


int main(void)
{
    X u[5];

    int res=f<5,5>(u);
    return 0;
}

Of course you would have to take care of other (potentially dangerous) special cases like n={0,1,2,3} and you're probably better off using unsigned int's instead of ints.

淡水深流 2024-09-14 08:08:10

所以我的问题是:有没有
强制转换数组的类型安全方法
将一种大小转换为较小大小的数组
其中数组类型相同?

不,我认为该语言根本不允许您执行此操作:请考虑将 int[10] 转换为 int[5]。但是,您始终可以获得指向它的指针,但我们不能“欺骗”编译器认为固定大小具有不同的维度数。

如果您不打算使用 std::vector 或其他一些可以在运行时正确识别内部点数的容器,并在一个函数中方便地完成这一切,而不是根据元素数量调用两个函数重载,与其尝试进行疯狂的转换,不如将其视为一种改进:

void RectToPointArray(const degRect& rect, degPoint* points, unsigned int size);

如果您打算使用数组,您仍然可以定义一个像这样的通用函数:

template <class T, size_t N>
std::size_t array_size(const T(&/*array*/)[N])
{
    return N;
}

...并在调用 RectToPointArray 时使用它来传递参数'尺寸'。然后你就有了一个可以在运行时确定的大小,并且很容易使用 size - 1,或者更适合这种情况,只需放置一个简单的 if 语句来检查是否有 5 个元素或 4 个元素 稍后,

如果您改变主意并使用 std::vector、Boost.Array 等,您仍然可以使用这个相同的旧函数而无需修改它。它只要求数据是连续且可变的。您可以对此感兴趣并应用非常通用的解决方案,例如仅需要前向迭代器。但我认为这个问题还没有复杂到需要这样的解决方案:这就像用大炮杀死一只苍蝇一样;苍蝇拍就可以了

如果您确实确定了现有的解决方案,那么执行此操作很容易:

template <size_t N>
void RectToPointArray(const degRect& rect, degPoint(&points)[N])
{
    assert(N >= 4 && "points requires at least 4 elements!");
    points[0].lat = rect.nw.lat; points[0].lon = rect.nw.lon;
    points[1].lat = rect.nw.lat; points[1].lon = rect.se.lon;
    points[2].lat = rect.se.lat; points[2].lon = rect.se.lon;
    points[3].lat = rect.se.lat; points[3].lon = rect.nw.lon;

    if (N >= 5)
        points[4].lat = rect.nw.lat; points[4].lon = rect.nw.lon;
}

是的,有一个不必要的运行时检查,但尝试在编译时执行此操作可能类似于尝试将东西从手套箱中取出提高汽车的燃油效率。由于 N 是编译时常量表达式,编译器可能会识别出当 N < 时条件始终为 false。 5 然后删除整段代码。

So my question is: is there a
type-safe way of casting an array of
one size to an array of a smaller size
where the array type is the same?

No. I don't think the language allows you to do this at all: consider casting int[10] to int[5]. You can always get a pointer to it, however, but we can't 'trick' the compiler into thinking a fixed-sized has a different number of dimensions.

If you're not going to use std::vector or some other container which can properly identify the number of points inside at runtime and do this all conveniently in one function instead of two function overloads which get called based on the number of elements, rather than trying to do crazy casts, consider this at least as an improvement:

void RectToPointArray(const degRect& rect, degPoint* points, unsigned int size);

If you're set on working with arrays, you can still define a generic function like this:

template <class T, size_t N>
std::size_t array_size(const T(&/*array*/)[N])
{
    return N;
}

... and use that when calling RectToPointArray to pass the argument for 'size'. Then you have a size you can determine at runtime and it's easy enough to work with size - 1, or more appropriate for this case, just put a simple if statement to check if there are 5 elements or 4.

Later if you change your mind and use std::vector, Boost.Array, etc. you can still use this same old function without modifying it. It only requires that the data is contiguous and mutable. You can get fancy with this and apply very generic solutions that, say, only require forward iterators. Yet I don't think this problem is complicated enough to warrant such a solution: it'd be like using a cannon to kill a fly; fly swatter is okay.

If you're really set on the solution you have, then it's easy enough to do this:

template <size_t N>
void RectToPointArray(const degRect& rect, degPoint(&points)[N])
{
    assert(N >= 4 && "points requires at least 4 elements!");
    points[0].lat = rect.nw.lat; points[0].lon = rect.nw.lon;
    points[1].lat = rect.nw.lat; points[1].lon = rect.se.lon;
    points[2].lat = rect.se.lat; points[2].lon = rect.se.lon;
    points[3].lat = rect.se.lat; points[3].lon = rect.nw.lon;

    if (N >= 5)
        points[4].lat = rect.nw.lat; points[4].lon = rect.nw.lon;
}

Yeah, there is one unnecessary runtime check but trying to do it at compile time is probably analogous to taking things out of your glove compartment in an attempt to increase your car's fuel efficiency. With N being a compile-time constant expression, the compiler is likely to recognize that the condition is always false when N < 5 and just eliminate that whole section of code.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文