【问题标题】:If a function returns an array by reference is it a decay to pointer?如果一个函数通过引用返回一个数组,它是对指针的衰减吗?
【发布时间】:2018-03-16 10:50:03
【问题描述】:

在下面的示例代码中,ComputeSomething() 返回对数组的引用。

我被要求使用 C++ 核心指南(MSVC 工具链上的 NuGet 包)作为额外的静态分析工具。

ComputeSomething() 的返回行上,静态分析工具警告说存在一个指向指针衰减的数组。我假设意图是更改它以使衰减明确(类似于&(computed_values[0])),但这会破坏通过引用返回数组的点。

对于这种通过引用返回数组的特定用例,这是一个有效的警告还是噪音?

(假设 C++98 约束)

float (&ComputeSomething( const seed_t(&seed_data)[num_of_elems_seeded] ))[num_of_elems_calculated]{

    static float computed_values[num_of_elems_calculated];

    // do something...

    return computed_values;
}

【问题讨论】:

  • 请在 SO 上简化您的代码 sn-ps!没有人需要您的命名空间、一长串初始化变量、您的计算等等。您只想返回参考并获得警告。所以请把你的例子简化为这个!
  • 阅读minimal reproducible example了解更多
  • 函数周围多余的(无意义的)括号是怎么回事?这很奇怪。
  • @JesperJuhl " int (&foo())[bar]; " 声明了一个函数 foo ,它返回对大小为 bar 的 int 数组的引用,我不确定这是否能回答您的问题/评论?
  • 没有看到你如何调用我们无法回答的函数。函数本身返回一个引用。如果它衰减与否应该取决于你用它做什么。

标签: c++ c++98 cpp-core-guidelines


【解决方案1】:

以下方法:

float (&ComputeSomething())[num_of_elems_calculated]{

    static float computed_values[num_of_elems_calculated];

    // do something...

    return computed_values;
}

与静态分析工具中可能发生的情况不同,指针衰减的潜在数组不是在方法内部确定,而是在调用者调用函数时确定:

// expected to decay
float *some_computation = ComputeSomething(); 

// not expected to decay to pointer
float (&safer_computation)[num_of_elems_calculated] = ComputeSomething();

感谢@Galik 对此的意见。

【讨论】:

    【解决方案2】:

    是的。

    ComputeSomething 函数中,computed_values 是一个数组。和往常一样,它可以自然地衰减为指向其第一个元素的指针。

    所以ComputeSomething 函数可以简化为

    const float* ComputeSomething(...){ ... }
    

    为了“保留”尺寸信息,然后能够像现在一样使用函数,调用者需要知道尺寸无论如何。这不是自动转移的东西。因此,使用指针不会有太大变化,除非您绝对想在数组上使用sizeof,但这可以通过使用您必须在变量声明中提供的大小来回避。使用指针肯定会简化语法,从而使代码更具可读性和可维护性。

    您也可以使用std::vector,它已内置大小信息。或者强制升级到可以使用 C++11 的更现代的编译器,然后使用 std::array 如果你想要编译时固定大小的数组。

    我不确定 std::pair 是否存在于 C++98 中,或者它是否存在于 C++03 中,但作为某种中间立场,您可以使用 std::pair<float*, size_t> 来返回一个指针 尺寸。

    最后,您始终可以使用typedef 为您的数组类型创建别名。然后你就不需要在任何地方显式指定大小了,它会大大简化语法:

    typedef computed_values_type[num_of_elems_calculated];
    
    ....
    
    computed_values_type& ComputeSomething(...) { ... }
    
    ...
    
    computed_values_type& computed_values = ComputeSomething(...);
    

    【讨论】:

    • @FantasticMrFox 类似:float (&result)[42] = ComputeSomething(...)' 也许?
    • ... 如果 ComputeSomething 没有返回对包含 42 个元素的数组的引用,则会出现编译错误。
    • 或者干脆auto& array = ComputeSomething(); int elemtCount = sizeof(array)/sizeof(array[0]);
    • 这是不正确的。 OPs 函数返回一个引用。只要将返回值分配给引用,它就不会衰减。
    • 你或 OP 都应该更新,问题中不再有MagicLookUp
    【解决方案3】:

    从编译的二进制文件的角度来看,对数组的引用只是指向第一个元素的旧指针。它与原始指针的区别仅在于类型本身,其中包含有关数组大小的信息。例如,它可以用于编译时大小检查、编译器警告或range-based for 循环

    【讨论】:

    • 这既没有解决所提出的问题,也不会是正确的,如果确实如此。您所描述的是一个合理的实现,任何给定的 C++ 编译器都可以选择。不过,这不是必需的。编译器可以选择在返回数组时发出完全不同的代码。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2020-01-12
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2016-10-25
    • 2012-12-12
    • 1970-01-01
    相关资源
    最近更新 更多