Разминка для мозгов: печать элементов массива

Дисклаймер: Надеюсь, что Череп простит мне использование своего бренда «Разминка для мозгов» ;)

Предлагаю решить одну простенькую задачку: вывести на печать все элементы массива. Решили? А теперь вопрос: сколькими способами это можно сделать?

Для единообразия используйте следующий шаблон:

// Здесь можно добавить необходимые заголовки

using namespace std;

// Здесь можно разместить необходимые функции, классы и т.п.

int main() {

    // это массив, который необходимо напечатать
    int ary[] = { 0, 2, 4, 6, 8, 1, 3, 5, 7, 9 };

    {
        // здесь разместить код одного варианта решения
    }

    return 0;
}

Числа на печать выводить через один пробел:

0 2 4 6 8 1 3 5 7 9

Строка заканчивается символом конца строки, возможно с пробелом перед ним.

Каждый вариант решения размещайте в отдельном блоке. Это предотвратит взаимное влияние локальных переменных, объявленных внутри блока, на другие варианты решений.


В отличие от других «разминок», я не знаю точного ответа на поставленный вопрос. Предлагайте варианты, и будем считать.

сколькими способами это можно сделать?

Бесчисленное множество вариантов.
Приведу три простеньких:

//g++  5.4.0

#include <iostream>
#include <algorithm>
#include <iterator>

int main() {

    int ary[] = { 0, 2, 4, 6, 8, 1, 3, 5, 7, 9 };

    {
        std::copy(std::begin(ary), std::end(ary), std::ostream_iterator<int>(std::cout, " "));
        std::cout << '\n';
    }
    {
        for(auto &e: ary)
            std::cout << e << ' ';
        std::cout << '\n';
    }
    {
        std::for_each(std::begin(ary), std::end(ary), [](auto x) { printf("%d ", x); });
        std::cout << '\n';
    }
}

http://rextester.com/FYQRR7126

Только cstdio еще не помешает включить.

Было бы интересно посмотреть и на «непростенькие» варианты.

Ещё пару вариантов

#include <iostream>
#include <algorithm>
#include <iterator>
#include <functional>

template <int I>
struct outArray_helper {
    template <class T, int N, class F> static
    void print(const T(& arr)[N], const F& f) {
        outArray_helper<I-1>::print(arr, f);
        f(arr[I-1]);
    }
};

template <>
struct outArray_helper<0> {
    template <class T, int N, class F> static
    void print(const T(& arr)[N], const F& f) {}
};


template <int N, class F>
void outIntArray(const int(& arr)[N], const F& f) {
    outArray_helper<N>::print(arr, f);
}

int main() {

    int ary[] = { 0, 2, 4, 6, 8, 1, 3, 5, 7, 9 };

    {
    using namespace std::placeholders;
    std::for_each(std::begin(ary), std::end(ary), std::bind(printf, "%d ", _1));
        std::cout << '\n';
    }
    {
        outIntArray(ary, [](auto x) { printf("%d ", x); });
        std::cout << '\n';
    }
}

http://rextester.com/QDBJX30789

#include <iostream>
#include <algorithm>
#include <iterator>

namespace details
{
    template<typename T, size_t N, size_t ... I>
    void array_out_impl(T (&arr)[N], std::index_sequence<I...>)
    {
        int fake[] = {
            ((std::cout << arr[I] << ' '), 0)...
        }; (void)fake;
    }

}


template<typename T, size_t N>
void array_out(T (&arr)[N])
{
    details::array_out_impl(arr, std::make_index_sequence<N>());
    std::cout << '\n';
}


int main() {

    int ary[] = { 0, 2, 4, 6, 8, 1, 3, 5, 7, 9 };

    array_out(ary);
}

http://rextester.com/ZDJQ63966

MasterOfAlteran, по варианту от 14 ноября. Я правильно понял, что в array_out_impl массив fake инициализируется нулями в количестве размера исходного массива, а как побочный результат происходит вывод на консоль элементов массива arr с участием операции ,?

Не понял зачем нужно

(void)fake;

Типа использование объявленной переменной? Что бы оптимизатор не порюхал? И не понял значения этого выражения. fake — это адрес первого элемента массива, который приводится к типу void. И что получается в результате?

Если этот код немного обфусцировать (только идентификаторы), то может вполне потянуть на самостоятельную «разминку для мозгов» типа «что делает этот код?».

Григорий, хотелось бы уточнить по вашему второму варианту. Я правильно понимаю, что функция print будет инстанцирована N-ное количество раз, где N — количество элементов в массиве ary?

Я правильно понял

Да. И любой адекватный компилятор при оптимизации его выбросит, оставив только вывод.

И что получается в результате?

Подавление предупреждения компилятора.
Иначе будет ругань на неиспользуемую переменную.
Такой прием много где используется.
Даже макросы делают подобные:

#define VAR_UNUSED(var) (void)var;

Например, таким же образом работает Q_UNUSED в Qt.

Если этот код немного обфусцировать (только идентификаторы), то может вполне потянуть на самостоятельную «разминку для мозгов» типа «что делает этот код?».

Вряд ли. Любой человек, знакомый с шаблонами поймет что здесь происходит без труда, а обфусцированный код никто и смотреть не будет — смысла нет, больше будешь каракули разбирать, а не в код вникать.

Переделка для C++17:

#include <iostream>
#include <utility>

namespace details
{
    template<typename T, size_t N, size_t ... I>
    void array_out_impl(T (&arr)[N], std::index_sequence<I...>)
    {
        ((std::cout << arr[I] << ' '), ...) << '\n';
    }

}


template<typename T, size_t N>
void array_out(T (&arr)[N])
{
    details::array_out_impl(arr, std::make_index_sequence<N>());
    std::cout << '\n';
}


int main() {

    int ary[] = { 0, 2, 4, 6, 8, 1, 3, 5, 7, 9 };

    array_out(ary);
}

Я правильно понимаю

В принципе, да. Рекурсивные шаблоны очень распространенный прием.

#include <iostream>
#include <utility>

template<typename T, size_t N>
struct ArrayWrapper
{
    ArrayWrapper(T (&arr_)[N]) 
        : arr(arr_)
    {
    }
    T (&arr)[N];
};


template<typename T, size_t N>
ArrayWrapper<T, N> make_array_wrapper(T (&arr)[N])
{
    return {arr};
}


template<typename T, size_t N>
std::ostream &operator<<(std::ostream &stream, const ArrayWrapper<T, N> wrapper)
{
    for (auto &e: wrapper.arr)
        stream << e << ' ';
    return stream << '\n';
}


int main() {

    int ary[] = { 0, 2, 4, 6, 8, 1, 3, 5, 7, 9 };
    std::cout << make_array_wrapper(ary);
}

http://rextester.com/FYQDF78100

Конкретно для этого примера можно сделать и так:

#include <iostream>


template<typename T, size_t N>
std::ostream &operator<<(std::ostream &stream, T (&arr)[N])
{
    for (auto &e: arr)
        stream << e << ' ';
    return stream << '\n';
}


int main() {

    int ary[] = { 0, 2, 4, 6, 8, 1, 3, 5, 7, 9 };
    std::cout << ary;
}

http://rextester.com/ELFTD94203

Переделка под C++17:

#include <iostream>

template<typename T, size_t N>
struct ArrayWrapper
{
    ArrayWrapper(T (&arr_)[N]) 
        : arr(arr_)
    {
    }
    T (&arr)[N];
};

template<typename T, size_t N>
std::ostream &operator<<(std::ostream &stream, const ArrayWrapper<T, N> wrapper)
{
    for (auto &e: wrapper.arr)
        stream << e << ' ';
    return stream << '\n';
}


int main() {

    int ary[] = { 0, 2, 4, 6, 8, 1, 3, 5, 7, 9 };
    std::cout << ArrayWrapper(ary);
}

Плоды усилий извращённых умов поражают. А как-нибудь простенько не судьба? Типа такого:

#include <iostream>

using namespace std;

int main() {

    int ary[] = { 0, 2, 4, 6, 8, 1, 3, 5, 7, 9 };

    {
        for (size_t i = 0; i < sizeof(ary) / sizeof(*ary); ++i)
            cout << ary[i] << ' ';
        cout << endl;
    }

    return 0;
}

А как-нибудь простенько не судьба?

Это слишком банально и не интересно, имхо.

Кстати говоря, я несколько удивлен. Во-первых, я думал, что будет множество как раз «простеньких» решений от новичков. Во-вторых, странно, что новички не приняли участия в разминке, хотя тема как раз для них. Кстати, кто-то из них сетовал, что «разминка для мозгов» больше не проводится. Видимо поэтому и не проводится.

MasterOfAlteran, по варианту от 18 ноября под С++17. Всё прекрасно работает с С++11 вот в таком варианте:

#include <iostream>

template<typename T, size_t N>
struct ArrayWrapper {
    ArrayWrapper(T(&arr_)[N]) : arr(arr_) { }
    T(&arr)[N];
};

template<typename T, size_t N>
std::ostream &operator<<(std::ostream &stream, const ArrayWrapper<T, N> wrapper) {
    for (auto &e : wrapper.arr)
        stream << e << ' ';
    return stream << '\n';
}


int main() {

    int ary[] = { 0, 2, 4, 6, 8, 1, 3, 5, 7, 9 };
    std::cout << ArrayWrapper<int, sizeof(ary) / sizeof(*ary)>(ary);
}

Но если последнюю строку заменить на

std::cout << ArrayWrapper<decltype(*ary), sizeof(ary) / sizeof(*ary)>(ary);

то начинает сильно ругаться по поводу невозможности создавать массив ссылок. Почему??

Почему??

Потому что decltype(*ary) даст тип int&,
а массив ссылок, как известно, создать нельзя.
Должно сработать:
C++14 — std::remove_reference_t<decltype(*ary)>
C++11 — typename std::remove_reference<decltype(*ary)>::type

Потому что decltype(*ary) даст тип int&

Почему? Должен быть же просто int?

А можно ли как-то узнать результат выполнения decltype()?

Должен быть же просто int?

Не должен.

Почему?

decltype для выражения дает ссылку.

А можно ли как-то узнать результат выполнения decltype()?

Смотря зачем это нужно.

decltype для выражения дает ссылку.

Сцуко ((

Смотря зачем это нужно.

Скажем в целях отладки при написании кода. Мне вот было совершенно не очевидно, что *ary или ary[0] будет иметь тип ссылки. Хотя, конечно, рассуждая логически, для выражения и должна быть ссылка. Просто я *ary как-то за выражение не посчитал: разыменование и разыменование... не сумма же ))

Просто я *ary как-то за выражение не посчитал

int x = 0;
//decltype(x) - int
//decltype((x)) - int&

Скажем в целях отладки при написании кода.

IDE, наверное, могут тип показать.
Но, для успешного метапрограммирования систему типов
и правила вывода типов нужно знать на зубок.

Внимание! Это довольно старый топик, посты в него не попадут в новые, и их никто не увидит. Пишите пост, если хотите просто дополнить топик, а чтобы задать новый вопрос — начните новый.

Ответить

Вы можете использовать разметку markdown для оформления комментариев и постов. Используйте функцию предпросмотра для проверки корректности разметки.

Пожалуйста, оформляйте исходный код в соответствии с правилами разметки. Для того, чтобы вставить код в комментарий, скопируйте его в текстовое поле ниже, после чего выделите то, что скопировали и нажмите кнопку «код» в панели инструментов. Иначе ваш код может принять нечитаемый вид.

Либо производите оформление кода вручную, следующим образом:

``` #include <iostream> using namespace std; int main() { // ... } ```

Предпросмотр сообщения

Ваше сообщение пусто.