V2EX = way to explore
V2EX 是一个关于分享和探索的地方
现在注册
已注册用户请  登录
V2EX 提问指南
jimages
V2EX  ›  问与答

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

  •  
  •   jimages · 2020-01-07 14:39:04 +08:00 · 975 次点击
    这是一个创建于 1563 天前的主题,其中的信息可能已经有所发展或是发生改变。

    按照定义 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 个参数,然后通过判断条件结束递归。可是为什么会编译出错呢?

    7 条回复    2020-01-07 15:06:16 +08:00
    geelaw
        1
    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
        2
    ipwx  
       2020-01-07 14:49:08 +08:00
    都用模板了,还是建议用特例化而不是用条件语句来实现多态。。。

    https://ideone.com/x2XsTz
    ipwx
        3
    ipwx  
       2020-01-07 14:50:27 +08:00
    哦不用类型特例化包一层,还是用楼上的函数重载吧。。。写 Python 写多了忘记了 C++ 有函数重载这回事情,看见编译器报错不让函数模板特例化就下意识上类型特例化了。
    jimages
        4
    jimages  
    OP
       2020-01-07 14:56:53 +08:00
    @geelaw 第一个方法的确可以解,这也是书上的标准解法。而至于第二个方法,我刚刚已经试过了,这样似乎是不行的?我的代码大概如下,https://paste.ubuntu.com/p/bCjScgBPm2/
    报错仍然为 no matching function for call to 'print',具体错误内容与之前的错误提示类似。
    geelaw
        5
    geelaw  
       2020-01-07 14:58:28 +08:00
    @jimages #4 显然第二个解法里第一个 overload 我忘记删除 template <typename T> 了
    jimages
        6
    jimages  
    OP
       2020-01-07 15:01:16 +08:00
    @geelaw 好的,感谢!
    jimages
        7
    jimages  
    OP
       2020-01-07 15:06:16 +08:00
    而至于 if (sizeof...(Args) == 0)为什么是错的,是因为这是运行时判断,而不是编译时判断,如果改成编译时判断(if constexpr)的话,就可以实现了。
    有劳各位:
    @ipwx
    @geelaw
    关于   ·   帮助文档   ·   博客   ·   API   ·   FAQ   ·   我们的愿景   ·   实用小工具   ·   3199 人在线   最高记录 6543   ·     Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 · 28ms · UTC 13:46 · PVG 21:46 · LAX 06:46 · JFK 09:46
    Developed with CodeLauncher
    ♥ Do have faith in what you're doing.