首页
学习
活动
专区
工具
TVP
发布
社区首页 >问答首页 >C++17可变模板折叠

C++17可变模板折叠
EN

Stack Overflow用户
提问于 2017-05-01 21:22:13
回答 5查看 7.9K关注 0票数 26

我不明白这为什么行不通。了解模板和可变表达式折叠的人可以解释一下发生了什么,并给出一个可行的解决方案吗?

代码语言:javascript
复制
#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);
}

它应该打印出每个参数,中间有一个空格,末尾有一个换行符。如果您删除了sep <<,但在打印时每个参数之间没有空格,那么它就会起作用。

EN

回答 5

Stack Overflow用户

回答已采纳

发布于 2017-05-01 21:31:48

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

代码语言:javascript
复制
(pack op ... op init)
(init op ... op pack)

您所拥有的是(std::cout << ... << sep << args),它不适合这两种形式。你需要像(cout << ... << pack)这样的东西,这就是为什么删除sep是有效的。

相反,您可以将逗号折叠起来:

代码语言:javascript
复制
((std::cout << sep << args), ...);

或者使用递归:

代码语言:javascript
复制
template <class A, class... Args>
void print(A arg, Args... args) {
    std::cout << arg;
    if constexpr (sizeof...(Args) > 0) {
        std::cout << sep;
        print(args...);
    }
}
票数 35
EN

Stack Overflow用户

发布于 2017-05-02 02:03:34

你真正想做的是:

代码语言:javascript
复制
std::string sep = " ";
std::string end = "\n";
(std::cout << ... << (sep << args)) << end;

因为你想让(sep << args)std::cout一起左折叠。这不起作用,因为sep << args根本不知道它正被流式传输到std::cout或流式传输;<<只有在左侧是流的情况下才是流式传输的。

简而言之,问题是sep << args不理解它是流媒体。

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

我们可以解决这个问题。

代码语言:javascript
复制
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;
}

live example。(我还为sep使用了一个文本,以确保我使用右值)。

ostreamer捕获对它是<<'d的东西的引用,然后当它被<<ostream时转储它们。

整个过程对编译器来说应该是透明的,所以一个好的优化器应该把涉及到的一切都去掉。

票数 11
EN

Stack Overflow用户

发布于 2018-10-11 06:43:59

另一种方法是下一种:

代码语言:javascript
复制
#include <iostream>

template<class U, class... T>
    void printSpaced(const U& u, const T&... args)
{   
     using std::cout;
     using std::endl;         

     ((cout << u) << ... << (cout << ' ', args)) << endl;   
}

这样你就不会得到前导/尾随空格

用法:

代码语言:javascript
复制
printSpaced(1, 2, "Hello", 4.5f); //Output 1 2 Hello 4.5 and no trailing space
票数 2
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/43719789

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档