c++中的 variadic template function 中的 Args 不能接受 0 个参数?

2020-01-07 14:39:04 +08:00
 jimages

按照定义 Args 应该是可以接受 0 个参数的,但是为什么如下的代码不能正确编译呢?( c++11)

#include <iostream>
#include <string>

template <typename T, typename... Args>
auto print(std::ostream& os, const T& t, const Args&... rest) -> decltype(os)
{
    os << t;
    if (sizeof...(Args) == 0)
        return os << t;
    else
        return print(os, rest...);
}

int main()
{
    print(std::cout, std::string("hepup"));
    print(std::cout, 1, 2.23434);
    print(std::cout, "hello", '\n', 3.134, true, 1e8);
    return 0;
}

错误提示的是

recursive.cc:16:16: error: no matching function for call to 'print'
        return print(os, rest...);
               ^~~~~
recursive.cc:21:5: note: in instantiation of function template specialization
      'print<std::__1::basic_string<char>>' requested here
    print(std::cout, std::string("hepup"));
    ^
recursive.cc:10:6: note: candidate function template not viable: requires at least 2
      arguments, but 1 was provided
auto print(std::ostream& os, const T& t, const Args&... rest) -> decltype(os)
     ^
1 error generated.

诸如此类的。

按照标准A template parameter pack is a template parameter that accepts zero or more template arguments (non-types, types, or templates)那么应该是可以接受 0 个参数,然后通过判断条件结束递归。可是为什么会编译出错呢?

1064 次点击
所在节点    问与答
7 条回复
geelaw
2020-01-07 14:47:50 +08:00
因为一个对 print(os) 的调用总是会产生,所以出错。

一种解法是用 if constexpr 阻止不发生的路径的代码生成。
另一种是用重载

template <typename T>
std::ostream &print(std::ostream &os, T const &t) { return os << t; }

template <typename T, typename ...TRest>
std::ostream &print(std::ostream &os, T const &t, const TRest &...rest) { return print(os << t, rest...); }

第一个版本更加优先。

更自然的解法是允许 0 个东西被打印:

template <typename T>
std::ostream &print(std::ostream &os) { return os; }

template <typename T, typename ...TRest>
std::ostream &print(std::ostream &os, T const &t, const TRest &...rest) { return print(os << t, rest...); }
ipwx
2020-01-07 14:49:08 +08:00
都用模板了,还是建议用特例化而不是用条件语句来实现多态。。。

https://ideone.com/x2XsTz
ipwx
2020-01-07 14:50:27 +08:00
哦不用类型特例化包一层,还是用楼上的函数重载吧。。。写 Python 写多了忘记了 C++ 有函数重载这回事情,看见编译器报错不让函数模板特例化就下意识上类型特例化了。
jimages
2020-01-07 14:56:53 +08:00
@geelaw 第一个方法的确可以解,这也是书上的标准解法。而至于第二个方法,我刚刚已经试过了,这样似乎是不行的?我的代码大概如下,https://paste.ubuntu.com/p/bCjScgBPm2/
报错仍然为 no matching function for call to 'print',具体错误内容与之前的错误提示类似。
geelaw
2020-01-07 14:58:28 +08:00
@jimages #4 显然第二个解法里第一个 overload 我忘记删除 template <typename T> 了
jimages
2020-01-07 15:01:16 +08:00
@geelaw 好的,感谢!
jimages
2020-01-07 15:06:16 +08:00
而至于 if (sizeof...(Args) == 0)为什么是错的,是因为这是运行时判断,而不是编译时判断,如果改成编译时判断(if constexpr)的话,就可以实现了。
有劳各位:
@ipwx
@geelaw

这是一个专为移动设备优化的页面(即为了让你能够在 Google 搜索结果里秒开这个页面),如果你希望参与 V2EX 社区的讨论,你可以继续到 V2EX 上打开本讨论主题的完整版本。

https://www.v2ex.com/t/635796

V2EX 是创意工作者们的社区,是一个分享自己正在做的有趣事物、交流想法,可以遇见新朋友甚至新机会的地方。

V2EX is a community of developers, designers and creative people.

© 2021 V2EX