Как передать временный массив?

Как я могу передать временный массив? Я хочу сделать что-то вроде этого:

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

Нужен ли литерал положительного целого числа в фигурных скобках параметра функции []? Если я включу этот литерал, будет ли он ограничивать массивы, которые я могу передать, только массивами такого размера? Кроме того, как передать элементы массива по ссылке rvalue или ссылке const? Поскольку приведенный выше пример не компилируется, я предполагаю, что тип параметра функции int&&[] или const int&[] не будет работать.


person CodeBricks    schedule 19.02.2017    source источник
comment
Вы можете использовать шаблон, чтобы определить размер.   -  person πάντα ῥεῖ    schedule 19.02.2017
comment
почему бы не использовать std::array или std::vector?   -  person Charles    schedule 19.02.2017
comment
@ c650, я знаю, как ими пользоваться. Я просто хочу узнать о массивах в стиле C.   -  person CodeBricks    schedule 19.02.2017
comment
связанные: stackoverflow.com/questions /6376000/   -  person 463035818_is_not_a_number    schedule 19.02.2017
comment
Вы можете принять мой обновленный ответ, потому что он фактически позволяет вам использовать синтаксис, который вы изначально запрашивали. Кроме того, он более общий, чем ответ Керрека.   -  person zett42    schedule 20.02.2017


Ответы (2)


Во-первых, вы не можете передавать массивы как значения prvalue, поэтому ваша функция должна принимать ссылку. Во-вторых, размер массива является частью типа, поэтому ваша функция, вероятно, должна быть частью шаблона. В-третьих, запись временных массивов лексически немного глупа, поэтому вам нужно немного шума.

Собрав все это вместе, следующее должно работать

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<4>{1, 2, 3, 4}) (Вы не можете вывести параметр шаблона из инициализатора.) Редактировать: Спасибо Jarod42 за указание на то, что на самом деле вполне возможно вывести аргумент шаблона из фигурной скобки. список; псевдоним типа не требуется.

person Kerrek SB    schedule 19.02.2017
comment
В вашем первом фрагменте, как я могу вызвать функцию без объявления псевдонима? - person CodeBricks; 19.02.2017
comment
Этот шаблон псевдонима для массивов уже существует: std::array<int, 3>{ 1, 2, 3 } - person zett42; 19.02.2017
comment
@ zett42: Нет, это шаблон класса. - person Kerrek SB; 20.02.2017
comment
@CodeBricks: вы не можете. Вот почему я сказал, что вам нужен лексический шум, вы не можете произнести этот тип по буквам на месте. - person Kerrek SB; 20.02.2017
comment
Мне просто пришло в голову, что у нас может быть хороший синтаксис sum({ 1, 2, 3 }) при предоставлении перегрузки с параметром std::initializer_list. Я обновлю свой ответ соответственно. - person zett42; 20.02.2017
comment
Псевдоним кажется ненужным Демо. - person Jarod42; 12.09.2019
comment
@Jarod42: Отличное замечание, спасибо! Я не уверен, как я решил, что это не может быть выведено... - person Kerrek SB; 13.09.2019

Я предлагаю сделать функцию суммы шаблоном, который принимает любой диапазон вместо того, чтобы ограничивать его массивами. Таким образом, вы можете использовать функцию со стандартными контейнерами, такими как std::vector, std::set или даже с контейнерами, определенными пользователем.

Для моего решения требуется библиотека boost.range, используя повышение сегодня? Диапазоны даже считаются добавленными в стандартную библиотеку.

#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< Range >::type sum( const Range& range )

  • Шаблон boost::range_value используется для определения типа элементов диапазона. Таким образом, мы можем использовать sum() не только для целых чисел, но и для всего, для чего определено operator +=! Вы можете видеть в моем примере, что мы можем даже «складывать» (конкатенировать) строки вместе. :D

  • Перегрузка, принимающая параметр std::initializer_list, наконец, делает возможным простой синтаксис, когда мы можем вызывать sum({ 1, 2, 3 }) по запросу OP. Эта перегрузка необходима, поскольку универсальная перегрузка не выводит тип аргумента initializer_list (см. также initializer_list и вывод типа шаблона )

Демонстрация:

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

person zett42    schedule 19.02.2017