折叠表达式和cout
Fold expressions and cout
我有以下有效的代码,但我对它的工作原理感到困惑。
template<typename ...Args>
void print(Args&&... args) {
(std::cout << ... << std::forward<Args>(args)) << '\n';
}
int main()
{
print(1,2.0,"3");
}
输出:
123
我的困惑:
我希望打印 321。
我想要这个订单:
cout << forward(args) << ...
但我无法编译...
用一些小技巧,(目前没有比这更好的了)你可以做以下事情:
不确定是否有直接的方法
// Your original function
template<typename ...Args>
void print(Args&&... args) {
(std::cout << ... << std::forward<Args>(args)) << '\n';
}
template<typename ...Args>
struct changeorder;
template<>
struct changeorder<>
{
template<typename ...OtherArgs>
static void invoke(OtherArgs const&... otherargs)
{
print(otherargs...);
}
};
template<typename T, typename ...Args>
struct changeorder<T, Args...>
{
template<typename ...OtherArgs>
static void invoke(T const& t, Args const&... args,
OtherArgs const&... otherargs)
{
// 1st parameter send first
changeorder<Args...>::invoke(args..., t, otherargs...);
}
};
template<typename A, typename ...Args>
void reverseprint(A const& a, Args const&... args)
{
changeorder<Args...>::invoke(args..., a);
}
...
的位置指定了左结合性或右结合性,但不会改变参数的顺序 - 它允许您在 (std::cout << x) << y
和 std::cout << (x << y)
之间进行选择。后者可能无法编译。
如果你想以相反的顺序打印值,你需要使用一些技巧。这是示例:
#include <type_traits>
#include <iostream>
template <typename T>
struct ReversePrinter
{
ReversePrinter(T val) : val(val) { }
template <typename U>
ReversePrinter<T> operator<<(const ReversePrinter<U>& other) const
{
std::cout << other.val;
return *this;
}
T val;
};
template <typename T>
std::ostream& operator<<(std::ostream& stream, const ReversePrinter<T>& val)
{
return stream << val.val;
}
template <typename... Args>
void print(Args... args)
{
std::cout << (ReversePrinter(args) << ...);
}
int main()
{
print(100, 200, 300.0); //prints 300200100
}
模板魔术的标准首选解决方案是std::index_sequence
。
为了使参数可索引,可以使用 std::tuple
.
template <std::size_t... N, class T>
void print_reverse_impl(std::index_sequence<N...>, std::ostream& os, T t) {
(os << ... << std::get<std::tuple_size_v<T> - N - 1>(t));
}
template <class... T>
void print_reverse(std::ostream& os, T&&... t) {
print_reverse_impl(std::make_index_sequence<sizeof...(t)>(), os, std::forward_as_tuple(t...));
}
不过,如果您的工具箱中有 (您真的应该),这会更简单:
template <class... T>
void print_reverse(std::ostream& os, T&&... t) {
static_for<sizeof...(t)>([&](auto n){
os << std::get<sizeof...(t) - n - 1>(std::forward_as_tuple(t...));
});
}
对于 C++20,也可以这样写:
void print_reverse(std::ostream& os, auto&&... t) {
[&]<auto... N>(std::index_sequence<N...>, auto all){
(os << ... std::get<sizeof...(t) - N - 1>(all));
}(std::make_index_sequence<sizeof...(t)>(), std::forward_as_tuple(t...));
}
顺便说一句,我删除了对 std::forward
的所有调用,因为这些右值引用无论如何都会被标准库缩减为左值引用。
折叠表达式尊重您使用的运算符的优先级和结合性。但是对于某些操作员,您可以进行更有创意的左右折叠。唯一需要考虑的变量是操作数的顺序。 C++17 在赋值运算符的左右两侧引入了先于发生的关系,因此它们的行为更加直观。右侧和所有相关的副作用必须首先发生。
因此,针对您的问题的完全独立的解决方案可能如下所示:
template <typename... Args>
void print(Args&& ...args) {
int dum = 0;
(... = (std::cout << args, dum));
}
在这里,live。
它使用逗号打印,同时以强制我们想要的评估顺序的方式将 dum
分配给自身。
我有以下有效的代码,但我对它的工作原理感到困惑。
template<typename ...Args>
void print(Args&&... args) {
(std::cout << ... << std::forward<Args>(args)) << '\n';
}
int main()
{
print(1,2.0,"3");
}
输出:
123
我的困惑:
我希望打印 321。
我想要这个订单:
cout << forward(args) << ...
但我无法编译...
用一些小技巧,(目前没有比这更好的了)你可以做以下事情:
不确定是否有直接的方法
// Your original function
template<typename ...Args>
void print(Args&&... args) {
(std::cout << ... << std::forward<Args>(args)) << '\n';
}
template<typename ...Args>
struct changeorder;
template<>
struct changeorder<>
{
template<typename ...OtherArgs>
static void invoke(OtherArgs const&... otherargs)
{
print(otherargs...);
}
};
template<typename T, typename ...Args>
struct changeorder<T, Args...>
{
template<typename ...OtherArgs>
static void invoke(T const& t, Args const&... args,
OtherArgs const&... otherargs)
{
// 1st parameter send first
changeorder<Args...>::invoke(args..., t, otherargs...);
}
};
template<typename A, typename ...Args>
void reverseprint(A const& a, Args const&... args)
{
changeorder<Args...>::invoke(args..., a);
}
...
的位置指定了左结合性或右结合性,但不会改变参数的顺序 - 它允许您在 (std::cout << x) << y
和 std::cout << (x << y)
之间进行选择。后者可能无法编译。
如果你想以相反的顺序打印值,你需要使用一些技巧。这是示例:
#include <type_traits>
#include <iostream>
template <typename T>
struct ReversePrinter
{
ReversePrinter(T val) : val(val) { }
template <typename U>
ReversePrinter<T> operator<<(const ReversePrinter<U>& other) const
{
std::cout << other.val;
return *this;
}
T val;
};
template <typename T>
std::ostream& operator<<(std::ostream& stream, const ReversePrinter<T>& val)
{
return stream << val.val;
}
template <typename... Args>
void print(Args... args)
{
std::cout << (ReversePrinter(args) << ...);
}
int main()
{
print(100, 200, 300.0); //prints 300200100
}
模板魔术的标准首选解决方案是std::index_sequence
。
为了使参数可索引,可以使用 std::tuple
.
template <std::size_t... N, class T>
void print_reverse_impl(std::index_sequence<N...>, std::ostream& os, T t) {
(os << ... << std::get<std::tuple_size_v<T> - N - 1>(t));
}
template <class... T>
void print_reverse(std::ostream& os, T&&... t) {
print_reverse_impl(std::make_index_sequence<sizeof...(t)>(), os, std::forward_as_tuple(t...));
}
不过,如果您的工具箱中有
template <class... T>
void print_reverse(std::ostream& os, T&&... t) {
static_for<sizeof...(t)>([&](auto n){
os << std::get<sizeof...(t) - n - 1>(std::forward_as_tuple(t...));
});
}
对于 C++20,也可以这样写:
void print_reverse(std::ostream& os, auto&&... t) {
[&]<auto... N>(std::index_sequence<N...>, auto all){
(os << ... std::get<sizeof...(t) - N - 1>(all));
}(std::make_index_sequence<sizeof...(t)>(), std::forward_as_tuple(t...));
}
顺便说一句,我删除了对 std::forward
的所有调用,因为这些右值引用无论如何都会被标准库缩减为左值引用。
折叠表达式尊重您使用的运算符的优先级和结合性。但是对于某些操作员,您可以进行更有创意的左右折叠。唯一需要考虑的变量是操作数的顺序。 C++17 在赋值运算符的左右两侧引入了先于发生的关系,因此它们的行为更加直观。右侧和所有相关的副作用必须首先发生。
因此,针对您的问题的完全独立的解决方案可能如下所示:
template <typename... Args>
void print(Args&& ...args) {
int dum = 0;
(... = (std::cout << args, dum));
}
在这里,live。
它使用逗号打印,同时以强制我们想要的评估顺序的方式将 dum
分配给自身。