13

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.

5
  • 1
    You can use a template to deduce the size. Commented Feb 19, 2017 at 20:05
  • 1
    why not use std::array or std::vector? Commented Feb 19, 2017 at 20:06
  • 1
    @c650 , I know how to use those. I just want to learn about C-style arrays. Commented Feb 19, 2017 at 20:07
  • 1
    related: stackoverflow.com/questions/6376000/… Commented Feb 19, 2017 at 20:07
  • You may consider accepting my updated answer because it actually allows you to use the syntax you originally requested. In addition it is more generic than Kerrek's answer. Commented Feb 19, 2017 at 23:34

2 Answers 2

17

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()
{
    std::cout << sum({1, 2, 3}) << "\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.) Edit: Thanks to Jarod42 for pointing out that it is in fact perfectly possible to deduce the template argument from a braced list; no type alias is needed.

Sign up to request clarification or add additional context in comments.

8 Comments

In your first snippet, how can I call the function without the alias declaration?
This alias template for arrays already exists: std::array<int, 3>{ 1, 2, 3 }
@zett42: No, that's a class template.
@CodeBricks: You cannot. That's why I said you need the lexical noise, you cannot spell this type out in-place.
It just came to my mind we can have the nice syntax of sum({ 1, 2, 3 }) when providing an overload with std::initializer_list parameter. I will update my answer accordingly.
|
0

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

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.