从 std::array 获取对原始数组的引用

Posted

技术标签:

【中文标题】从 std::array 获取对原始数组的引用【英文标题】:Getting reference to the raw array from std::array 【发布时间】:2017-01-04 11:44:45 【问题描述】:

获取对std::array 的底层原始 (C) 数组的引用的规范方法是什么?

data() 方法只返回一个原始指针,这使得它不适合,例如用于传递给接受对已知大小的原始数组的引用的函数。

另外,data() 返回一个原始指针而不是对底层原始数组的引用是否有充分的理由,或者这只是一个疏忽?

【问题讨论】:

传递对大小数组的引用很棘手,并且没有被广泛使用;为什么会这样,因为您只会使用原始数组与 C 代码交互,而 C 代码无论如何都没有引用? @WhiZTiM:OP 意味着无法将std::array<int,5> 转换为可以传递给myFunc( int (&k)[5]) 的东西。 (对 5 个整数数组的引用)。 @kabanus:不,不是。 @kabanus - C 语言中的这些“微妙之处”是正确将多维数组传递给函数与得到奇怪的指针转换错误或段错误之间的区别。 @k​​​​​​​​​​​​​abanus:这不是“微妙”。您提出了不正确的主张,并且该主张助长了误解。例如,这正是为什么您“不明白 [我们] 引用 [an array] 的意思”的原因,因为您认为数组(在任何一种语言中)都不是它们。 【参考方案1】:

获取 std::array 的底层 raw (C) 的规范方法是什么 数组?

无法获取底层 C 数组。

另外,data() 返回原始指针是否有充分的理由,而不是 对底层原始数组的引用,或者这只是一个疏忽?

它倒退了:std::array 没有充分的理由提供底层 C 数组。正如您已经说过的,C 数组只有在函数获得对 C 数组的引用时才有用(通过原始指针)。

你上一次使用函数是什么时候:

void foo(int (&arr)[5])

我?绝不。除了获取数组的大小(并拒绝指针)之外,我从未见过带有 C 数组引用参数的函数:

template <class T, std::size_t N>
auto safe_array_size(T (&)[N])  return N; 

让我们深入了解为什么不使用对数组的参数引用。

对于初学者来说,由于数组到指针的衰减和缺少引用类型,从 C 区域指针和单独的大小参数是传递数组的唯一方法。

在 C++ 中,有 C 数组的替代品,例如 std::vectorstd::array。但即使你有一个(旧版)C 数组,你也有两种情况:

如果将其传递给 C 函数,则没有引用选项,因此您只能使用指针 + 大小 当您想将其传递给 C++ 函数时,惯用的 C++ 方法是传递 begin + end 指针。

首先,begin + end 迭代器是通用的,它接受任何类型的容器。但是,当您想避免使用模板时,看到对std::vector 的引用并不少见,那么如果您有一个 C 数组,为什么不引用呢?因为一个很大的缺点:你必须知道数组的大小:

void foo(int (&arr)[5])

这是极其有限的。

要解决这个问题,您需要将其设为模板:

template <std::size N>
void foo(int (&arr)[N])

这超出了避免模板的目的,因此您最好使用 begin + end 模板迭代器。


在某些情况下(例如,仅对 2 或 3 个具有 相同的语义,所以它们不应该是单独的参数) 需要特定的数组大小,并使函数通用 没有意义。在这些情况下,指定数组的大小 保证安全,因为它只允许传入一个数组 在编译时正确的大小;因此它是有利的,不是 “大缺点”

(C 和)C++ 的优点之一是适用范围广。所以是的,你总会发现一些领域以独特的方式使用或需要某种独特的功能。话虽如此,即使在您的示例中,我仍然会回避数组。当您有固定数量的不应该在语义上分离的值时,我认为大多数时候结构将是数组的正确选择(例如glm::mat4 而不是float[4])。

但我们不要忘记std::array 是什么:C 数组的现代替代品。我在分析选项时学到的一件事是没有绝对的“优于”。总有一个“依赖”。但不是在这种情况下:std::array 应该毫无疑问地替换接口中的 C 数组。因此,在需要固定大小的容器作为参考参数的极少数情况下,当您已经拥有std::array 时,鼓励使用 C 数组是没有意义的。因此,需要公开std::array 的底层 C 数组的唯一有效情况是一些具有 C 数组引用参数的旧库。但我认为从大局来看,将其添加到界面中是不合理的。新代码应该使用结构(顺便说一句,std::tuple 越来越容易被每个标准使用)或std::array

【讨论】:

在某些情况下(例如,仅对具有相同语义的 2 或 3 个值进行数学计算,因此它们不应该是单独的参数)需要特定的数组大小,并使函数泛型不会没道理。在这些情况下,指定数组的大小可以保证安全,因为它只允许在编译时传入正确大小的数组;因此这是有利的,不是“大缺点”。 我只想指出,即使在C区,it's possible to take a pointer to an array of a specific size, using nearly-identical syntax to taking a reference to an array of a specific size in C++;不幸的是,在我所知道的每个编译器中,将指针传递给不同大小的数组都被视为警告而不是错误。 (T(&amp;)[N])*a.data() 有效,参见live on coliru。但这并不能使它非常实用。【参考方案2】:

AFAIK,没有直接或类型安全的方法可以做到这一点,但是如果您需要传递给函数(带有无法更改为 std::array 的签名),一种解决方法是使用reinterpret_cast,如下所示:

some_function(*reinterpret_cast<int (*)[myarr.size()]>(myarr.data())));

如果你想让它更安全:

#include <array>

void passarray(int (&myarr)[5])  

template <typename ValueT, std::size_t size>  
using CArray = ValueT[size];  

template <typename ValueT, std::size_t size>  
CArray<ValueT, size> & c_array_cast(std::array<ValueT, size> & arg)  
                                      
    return *reinterpret_cast<CArray<ValueT,size>*>(arg.data());  
  

int main()
  
    std::array<int,5> myarr =  1,2,3,4,5 ;  

    passarray(*reinterpret_cast<int (*)[myarr.size()]>(myarr.data()));  
    passarray(c_array_cast(myarr));  

    return 0;  
  

【讨论】:

纯粹出于好奇,使用*reinterpret_cast&lt;int (*)[myarr.size()]&gt;(myarr.data()) 而不仅仅是reinterpret_cast&lt;int*&gt;(myarr.data()) 有什么特别的原因吗?【参考方案3】:

没有。

我明白为什么它会很有用,尤其是在处理遗留代码时,但从几十年前开始,我们就应该从这样的代码转向迭代器感知算法。而且在使用 C 代码时,无论如何您都必须使用指针。我认为这些是决定不提供此功能的因素。

如果可能,重写您的代码以接受std::array&lt;T, N&gt;&amp;

【讨论】:

【参考方案4】:

您可以将reinterpret_cast.data() 转成原始的,比如:

template <typename T, std::size_t N>
inline static decltype(auto) to_raw_array(const std::array<T, N> & arr_v) 
        return reinterpret_cast<const T(&) [N]>(*arr_v.data());

但这是一个丑陋的黑客。正如其他人已经建议的那样,我建议您按原样使用std::array

用法:

#include <cstdint>
#include <array>

template <typename T, std::size_t N>
inline static decltype(auto) to_raw_array(const std::array<T, N> & arr_v) 
        return reinterpret_cast<const T(&) [N]>(*arr_v.data());


void foo(const std::uint8_t(&buf)[5])
   // ...


int main(void)
   std::array<std::uint8_t, 5> arr = 1,2,3,4,5;
   foo(to_raw_array(arr));

【讨论】:

【参考方案5】:

为什么不通过std::array.begin()?在 SDL2 中工作:

int SDL_RenderDrawLines(SDL_Renderer *renderer, const SDL_Point *points, int count)

我要画的线:

std::array<SDL_Point, 8> a_line;

我是这样通过的:

SDL_RenderDrawLines(r_s_game.p_renderer, a_line.begin(), 8);

【讨论】:

这里唯一有用的答案

以上是关于从 std::array 获取对原始数组的引用的主要内容,如果未能解决你的问题,请参考以下文章

如何获取对数组中对象的引用[重复]

std::array 的项目无意中脱离上下文而更改

在 MeteorJS 中获取原始 mongo db 引用

js 不可变的原始值和可变的对象引用

是否有可能创建对结构数组元素的引用?

C++ 加载和存储优化以及堆对象