C++ 17 Variadic模板折叠

nic*_*b96 24 c++ fold variadic-templates c++17

我不明白为什么这不起作用.知道模板和可变参数表达式折叠的人能解释发生了什么并提供一个有效的解决方案吗?

#include <iostream>
#include <string>

template <typename... Args>
void print(Args... args)
{
    std::string sep = " ";
    std::string end = "\n";
    (std::cout << ... << sep << args) << end;
}

int main()
{
    print(1, 2, 3);
}
Run Code Online (Sandbox Code Playgroud)

它应该打印出每个args,其间有一个空格,最后一个换行符.如果你删除了它,sep <<但是在打印时每个参数之间没有空格.

Bar*_*rry 30

二进制fold-expression的语法必须是以下之一:

(pack op ... op init)
(init op ... op pack)
Run Code Online (Sandbox Code Playgroud)

你有什么(std::cout << ... << sep << args),不适合任何一种形式.你需要类似的东西(cout << ... << pack),这就是删除sep作品的原因.

相反,您可以折叠逗号:

((std::cout << sep << args), ...);
Run Code Online (Sandbox Code Playgroud)

或使用递归:

template <class A, class... Args>
void print(A arg, Args... args) {
    std::cout << arg;
    if constexpr (sizeof...(Args) > 0) {
        std::cout << sep;
        print(args...);
    }
}
Run Code Online (Sandbox Code Playgroud)


Vit*_*meo 15

这将工作,但它将打印一个尾随空格:

template <typename... Args>
void print(Args... args)
{
    std::string sep = " ";
    std::string end = "\n";

    ((std::cout << args << sep), ...) << end;
}
Run Code Online (Sandbox Code Playgroud)

直播wandbox示例


在这种情况下,正在执行逗号运算符的折叠,从而产生如下扩展:

// (pseudocode)
(std::cout << args<0> << sep), 
(std::cout << args<1> << sep),
(std::cout << args<2> << sep), 
...,
(std::cout << args<N> << sep), 
Run Code Online (Sandbox Code Playgroud)


Yak*_*ont 11

你真正想做的是:

std::string sep = " ";
std::string end = "\n";
(std::cout << ... << (sep << args)) << end;
Run Code Online (Sandbox Code Playgroud)

因为你想要(sep << args)左折std::cout.这不起作用,因为sep << args不知道它正在流式传输std::cout或流式传输; <<如果左侧是流,则仅流式传输.

简而言之,问题是sep << args不明白它是流媒体.

你的另一个问题是lambda不够.

我们可以解决这个问题

template<class F>
struct ostreamer_t {
    F f;
    friend std::ostream& operator<<(std::ostream& os, ostreamer_t&& self ) {
        self.f(os);
        return os;
    }
    template<class T>
    friend auto operator<<(ostreamer_t self, T&& t) {
        auto f = [g = std::move(self.f), &t](auto&& os)mutable {
            std::move(g)(os);
            os << t;
        };
        return ostreamer_t<decltype(f)>{std::move(f)};
    }
};

struct do_nothing_t {
    template<class...Args>
    void operator()(Args&&...)const {}
};

const ostreamer_t<do_nothing_t> ostreamer{{}};

template <typename... Args>
void print(Args... args)
{
    std::string sep = " ";
    std::string end = "\n";
    (std::cout << ... << (ostreamer << sep << args)) << end;
}
Run Code Online (Sandbox Code Playgroud)

实例.(我还使用了一个文字sep来确保我使用rvalues).

ostreamer捕获它所引用的东西的引用<<,然后转而将它们<<转移到它ostream.

整个过程应该对编译器透明,因此一个体面的优化器应该蒸发所涉及的一切.