代码之家  ›  专栏  ›  技术社区  ›  Trevor Hickey

如何编写不能接受零参数的可变模板?

  •  1
  • Trevor Hickey  · 技术社区  · 11 年前

    这是一个打印参数的可变模板。

    #include <string>
    #include <iostream>
    
    void Output() {
        std::cout<<std::endl;
    }
    
    template<typename First, typename ... Strings>
    void Output(First arg, const Strings&... rest) {
        std::cout<<arg<<" ";
        Output(rest...);
    }
    
    int main() {
        Output("I","am","a","sentence");
        Output("Let's","try",1,"or",2,"digits");
        Output(); //<- I do not want this to compile, but it does.
    
        return 0;
    }
    

    有没有一种方法可以在不使用“无参数”调用的情况下获得此功能,并且不必每次编写两个函数?

    3 回复  |  直到 11 年前
        1
  •  3
  •   Daniel Frey    11 年前

    您可能希望保持第一个参数和其余参数的分隔,可以使用:

    template<typename First, typename ... Rest>
    void Output(First&& first, Rest&&... rest) {
        std::cout << std::forward<First>(first);
        int sink[]{(std::cout<<" "<<std::forward<Rest>(rest),0)... };
        (void)sink; // silence "unused variable" warning
        std::cout << std::endl;
    }
    

    请注意,我使用了完美转发来避免复制任何参数。上述方法还有避免递归的额外好处,因此可能会产生更好(更快)的代码。

    我的写作方式 sink 还保证表达式从 rest 从左到右进行评估-与只编写辅助函数的nave方法相比,这一点很重要 template<typename...Args>void sink(Args&&...){} .

    Live example

        2
  •  2
  •   aaronman    11 年前

    从转发类型的函数中调用该函数,并具有如下的static_assert:

    template <typename ... Args>                                                       
    void forwarder(Args ... args) {                                                    
        static_assert(sizeof...(args),"too small");                                    
        Output(args...);                                                               
    }  
    
        3
  •  2
  •   TAS    11 年前

    在我看来,有两个问题:

    1. 如何避免 Output() 不带参数的调用。
    2. 有没有一种更简单的方法来结束编译时递归?

    我对项目1的解决方案如下:

    template<typename T>
    void Output(const T & string) {
        std::cout<<string<<std::endl;
    }
    
    template<typename First, typename ... Strings>
    void Output(const First & arg, const Strings & ... rest) {
        std::cout<<arg<<" ";
        Output(rest...);
    }
    

    基本上,我不是在模板列表为空时结束递归,而是在它只包含一种类型时结束递归。上面的代码和问题中的代码有一个区别:if在最后一项之后没有输出任何空格。相反,它只是输出换行符。

    关于第二个问题,请参阅上面Daniel Frey的回答。我真的很喜欢这个解决方案,尽管我花了一些时间才掌握它(我对答案投了赞成票)。与此同时,我发现它使代码更难阅读/理解,因此更难维护。目前,除了小的个人代码片段,我不会在任何事情中使用该解决方案。