【问题标题】:How to pass a temporary array?如何传递一个临时数组?
【发布时间】:2017-07-09 00:06:16
【问题描述】:

如何传递一个临时数组?我想做这样的事情:

#include <iostream>

int sum(int arr[]) {
    int answer = 0;
    for (const auto& i : arr) {
        answer += i;
    }
    return answer;
}

int main() {
    std::cout << sum( {4, 2} ) << std::endl;       // error
    std::cout << sum( int[]{4, 2} ) << std::endl;  // error
}

在函数参数的大括号[] 中是否需要正整数文字?如果我包含该文字,它是否会限制我只能将哪些数组传递给该大小的数组?另外,如何通过右值引用或常量引用传递数组元素?因为上面的例子不能编译,所以我认为将函数的参数类型设置为int&amp;&amp;[]const int&amp;[] 将不起作用。

【问题讨论】:

  • 您可以使用模板来推断大小。
  • 为什么不使用std::arraystd::vector
  • @c650 ,我知道如何使用它们。我只是想了解 C 风格的数组。
  • 您可以考虑接受我更新的答案,因为它实际上允许您使用您最初请求的语法。此外,它比 Kerrek 的回答更通用。

标签: c++ arrays reference parameter-passing temporary


【解决方案1】:

首先,您不能将数组作为纯右值传递,因此您的函数需要引用。其次,数组的大小是类型的一部分,因此您的函数可能需要成为模板的一部分。 第三,写临时数组在词汇上有点傻,所以你需要一些噪音。

综上所述,以下应该可以工作

template <std::size_t N>
int sum(const int (&a)[N])
{
    int n = 0;
    for (int i : a) n += i;
    return n;
}

int main()
{
    std::cout << sum({1, 2, 3}) << "\n";
}

int main()
{
    using X = int[3];
    std::cout << sum(X{1, 2, 3}) << "\n";
}

语法噪音可以用别名模板稍微概括:

template <std::size_t N> using X = int[N];

用法:sum(X&lt;4&gt;{1, 2, 3, 4})(您不能从初始化程序中推导出模板参数。)编辑:感谢 Jarod42 指出实际上完全可以从花括号列表;不需要类型别名。

【讨论】:

  • 在你的第一个sn-p中,我如何在没有别名声明的情况下调用函数?
  • 此数组别名模板已存在:std::array&lt;int, 3&gt;{ 1, 2, 3 }
  • @zett42:不,这是一个类模板。
  • @CodeBricks:你不能。这就是为什么我说你需要词汇噪音,你不能就地拼写这种类型。
  • 我突然想到,当使用 std::initializer_list 参数提供重载时,我们可以使用 sum({ 1, 2, 3 }) 的好语法。我会相应地更新我的答案。
【解决方案2】:

我建议将 sum 函数设为接受任何 范围 的模板,而不是将其限制为数组。这样您就可以将该函数与标准容器(如 std::vector、std::set 甚至用户定义的容器)一起使用。

我的解决方案需要 boost.range 库,但今天谁没有使用 boost?范围甚至被认为是添加到标准库中。

#include <iostream>
#include <array>
#include <vector>
#include <string>
#include <boost/range.hpp>
#include <initializer_list>    

template< typename Range >
auto sum_impl( const Range& range ) -> typename boost::range_value< Range >::type
{
    typename boost::range_value< Range >::type result{};
    for( const auto& elem : range )
        result += elem;
    return result;
}

template< typename Range >
auto sum( const Range& range ) -> typename boost::range_value< Range >::type
{
    return sum_impl( range );
}

template< typename Elem >
Elem sum( const std::initializer_list< Elem >& range )
{
    return sum_impl( range );
}

int main()
{
    // Call the initializer_list overload
    std::cout << sum( { 1, 2, 3 } ) << "\n";
    std::cout << sum( { 1.0f, 2.1f, 3.2f } ) << "\n";

    // Call the generic range overload
    std::cout << sum( std::array<int,3>{ 1, 2, 3 } ) << "\n";
    std::cout << sum( std::vector<float>{ 1.0f, 2.1f, 3.2f } ) << "\n";
    std::cout << sum( std::vector<std::string>{ "a", "b", "c" } ) << "\n";  
}

一些解释:

  • 我使用auto 作为返回类型只是为了使函数声明更具可读性。你也可以这样写:

    typename boost::range_value&lt; Range &gt;::type sum( const Range&amp; range )

  • boost::range_value 模板用于推断范围内元素的类型。这样,我们不仅可以将 sum() 用于整数,还可以将任何定义了 operator += 的东西都使用!您可以在我的示例中看到我们甚至可以将字符串“添加”(连接)在一起。 :D

  • 采用std::initializer_list 参数的重载最终使简单的语法成为可能,我们可以按照OP 的要求调用sum({ 1, 2, 3 })。此重载是必需的,因为泛型重载不会推导出 initializer_list 参数类型(另请参阅initializer_list and template type deduction

演示:

http://coliru.stacked-crooked.com/a/80393e710fc355a6

【讨论】: