简体   繁体   中英

How to pass a temporary array?

How can I pass a temporary array? I want to do something like this:

#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
}

Do I need a positive integer literal in the function parameter's braces [] ? If I include that literal, will it limit what arrays I can pass to only arrays of that size? Also, how can I pass array elements by rvalue reference or const reference? Because the above sample doesn't compile, I presume making the function's parameter type int&&[] or const int&[] won't work.

First off, you cannot pass arrays as prvalues, so your function needs to take a reference. Second, the size of the array is part of the type, so your function probably needs to be part of a template. Third, writing array temporaries is lexically a bit silly, so you need some noise.

Putting it all together, the following ought to work

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

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

The syntactic noise can be generalized slightly with an alias template:

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

Usage: sum(X<4>{1, 2, 3, 4}) (You cannot have the template parameter deduced from the initializer.)

I suggest making the sum function a template that accepts any range instead of limiting it to arrays. This way you could use the function with standard containers like std::vector, std::set or even user-defined containers too.

My solution requires the boost.range library but who isn't using boost today? Ranges are even considered to be added to the standard library.

#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";  
}

Some explanations:

  • I'm using auto as return type just to make the function declaration more readable. You could also write it like this:

    typename boost::range_value< Range >::type sum( const Range& range )

  • The boost::range_value template is used to deduce the type of the elements of the range. This way we can use sum() not only for ints, but anything that has an operator += defined! You can see in my example that we can even "add" (concatenate) strings together. :D

  • The overload taking a std::initializer_list parameter finally makes the easy syntax possible where we can call sum({ 1, 2, 3 }) as requested by the OP. This overload is required because the generic overload won't deduce the initializer_list argument type (see also initializer_list and template type deduction )

Demo:

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

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM