简体   繁体   English

折叠表达和cout

[英]Fold expressions and cout

I have the following code that works, but I am confused about how it works. 我有以下代码可行,但我很困惑它是如何工作的。

template<typename ...Args>
void print(Args&&... args) {
    (std::cout << ... << std::forward<Args>(args)) << '\n';
}
int main()
{
    print(1,2.0,"3");
}

output: 输出:

123 123

My confusion: 我的困惑:

I would expect 321 printed. 我希望321打印。

I would like to have this order: 我想要这个订单:

cout << forward(args) << ... 

but I can not get that to compile... 但我无法编译......

The position of ... specifies left or right associativity, but doesn't change the order of arguments - it lets you choose between (std::cout << x) << y and std::cout << (x << y) . 的位置...指定向左或向右的相关性,但不改变参数的顺序-它可以让您选择(std::cout << x) << ystd::cout << (x << y) The later likely would not compile. 后者可能不会编译。

If you want to print values in the reversed order you need to use some trick. 如果要以相反的顺序打印值,则需要使用一些技巧。 Here is the example: 这是一个例子:

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

With few trickeries, (can't come anything better at the moment) you could do following: 只需很少的技巧,(目前无法做出更好的事情)你可以做到以下几点:

Not sure if there's any straight forward way 不确定是否有任何直接的方式

// 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);
}

Demo Here

The standard go-to solution for template-magic is std::index_sequence . template-magic的std::index_sequence解决方案是std::index_sequence
And for making arguments indexable one uses std::tuple . 为了使参数可索引,使用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...));
}

Still, if you have static_for() in your tool-box (you really should), this is simpler: 但是,如果你的static_for()static_for() (你真的应该),这更简单:

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...));
    });
}

With C++20, one could also write it as: 使用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...));
}

As an aside, I cut out all the calls to std::forward , because those rvalue-references would just be reduced down to lvalue-references by the standard-library anyway. 顺便说一下,我删除了对std::forward所有调用,因为这些rvalue-references只会被标准库减少到lvalue-references。

Fold expressions respect the precedence and associativity of the operator you use. 折叠表达式尊重您使用的运算符的优先级和关联性。 But for certain operators you can do more creative left and right folds. 但对于某些操作员,您可以进行更多创造性的左右折叠。 The only variable to account for is the sequencing of the operands. 要考虑的唯一变量是操作数的排序。 C++17 introduced a happens before relation between the right and left side of assignment operators, so they act more intuitively. C ++ 17在赋值运算符的右侧和左侧之间的关系之前引入了一个发生,因此它们的行为更直观。 The right side, and all associated side effects must happen first. 右侧和所有相关的副作用必须首先发生。

So a completely self contained solution to your question can look like this: 因此,对您的问题完全自包含的解决方案可能如下所示:

template <typename... Args>
void print(Args&& ...args) {
   int dum = 0;
   (... = (std::cout << args, dum));
}

Here it is, live . 在这里, 活着

It's using a comma to print, while assigning dum to itself in a way that forces the evaluation order we want. 它使用逗号进行打印,同时以强制我们想要的评估顺序的方式为自己分配dum

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM