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

Классический пример C ++ 17 выражений — это печать всех аргументов:

template<typename ... Args>
void print(Args ... args)
{
(cout << ... << args);
}

Пример:

print("Hello", 12, 234.3, complex<float>{12.3f, 32.8f});

Выход:

Hello12234.3(12.3,32.8)

Я хотел бы добавить новые строки в мой вывод. Тем не менее, я не могу найти хороший способ сделать это, лучшее, что я нашел до сих пор:

template<typename ... Args>
void print(Args ... args)
{
(cout << ... << ((std::ostringstream{} << args << "\n").str()));
}

Это, однако, не ноль накладных расходов, поскольку это создает временную ostringstream за каждый аргумент.

Следующие версии также не работают:

(cout << ... << " " << args);

error: expression not permitted as operand of fold expression

А также

(cout << ... << (" " << args));

error: invalid operands to binary expression

Я понимаю, почему последние две версии не работают.
Есть ли более элегантное решение этой проблемы, используя выражения сгиба?

9

Решение

repeat принимает функциональный объект fи вернуть новый объект функции. Возвращаемое значение запускает f на каждом из своих аргументов. Это «повторяется» f на каждом из его аргов.

template<class F>
auto repeat( F&& f ) {
return [f=std::forward<F>(f)](auto&&...args)mutable{
( void(f(args)), ... );
};
}

Использование:

repeat
( [](auto&&x){ std::cout << x << "\n"; } )
( args... );

Это использует выражения сгиба, но только косвенно. И, честно говоря, вы могли бы написать это в C ++ 14 (просто тело repeat было бы уродливее).

Мы также могли бы написать стример, который работает с << чтобы сделать это «более встроенным» и использовать выражения сложения напрямую:

template<class F>
struct ostreamer_t {
F f;
friend std::ostream& operator<<( std::ostream& os, ostreamer_t&& self ) {
std::move(self).f(os);
return os;
}
};

template<class F>
ostreamer_t<F> ostreamer( F&& f ) { return {std::forward<F>(f)}; }

тогда мы используем это так:

(std::cout << ... << ostreamer([&](auto&& os){ os << " " << args;}));

ostreamer принимает функциональный объект. Возвращает объект, который перегружается << такой, что когда вы передаете ему ostream слева, он вызывает объект функции с ostream.

Временный поток не создается.

Живые примеры.

5

Другие решения

Обновить: Агар Т.С.Комментарий ниже предоставил лучшее решение:

template<typename ... Args>
void print(Args ... args)
{
((cout << args << '\n'), ...);
}

Вы можете использовать сложить выражение над оператор запятой:

template<typename ... Args>
void print(Args ... args)
{
([](const auto& x){ cout << x << "\n"; }(args), ...);
}

Использование:

int main()
{
print("a", 1, 1000);
}

1

1000

(Примечание: также печатается завершающий символ новой строки.)


Объяснение:

  • [](const auto& x){ cout << x << "\n"; } это лямбда, которая дана x печать x а также '\n',

  • [](const auto& x){ cout << x << "\n"; }(args) немедленно вызывает лямбду с args,

  • ([](const auto& x){ cout << x << "\n"; }(args), ...) это складчатое выражение над оператор запятой это расширяется следующим образом:

    // (pseudocode)
    [](const auto& x){ cout << x << "\n"; }(args<0>),
    [](const auto& x){ cout << x << "\n"; }(args<1>),
    [](const auto& x){ cout << x << "\n"; }(args<2>),
    // ...
    [](const auto& x){ cout << x << "\n"; }(args<N>)
    
9

По вопросам рекламы [email protected]