迭代参数包

通常,我们需要对可变参数模板参数包中的每个元素执行操作。有很多方法可以做到这一点,并且使用 C++ 17 可以更轻松地读取和写入解决方案。假设我们只想打印包中的每个元素。最简单的解决方案是递归:

Version >= C++ 11

void print_all(std::ostream& os) {
    // base case
}

template <class T, class... Ts>
void print_all(std::ostream& os, T const& first, Ts const&... rest) {
    os << first;
    
    print_all(os, rest...);
}

我们可以使用扩展器技巧,在单个函数中执行所有流式传输。这样做的优点是不需要第二次过载,但缺点是可靠性低于恒星:

Version >= C++ 11

template <class... Ts>
void print_all(std::ostream& os, Ts const&... args) {
    using expander = int[];
    (void)expander{0,
        (void(os << args), 0)...
    };
}

有关其工作原理的说明,请参阅 TC 的优秀答案

Version >= C++ 17

使用 C++ 17,我们在我们的工具库中获得了两个强大的新工具来解决这个问题。第一个是 fold-expression:

template <class... Ts>
void print_all(std::ostream& os, Ts const&... args) {
    ((os << args), ...);
}

第二个是 if constexpr,它允许我们在一个函数中编写我们的原始递归解决方案:

template <class T, class... Ts>
void print_all(std::ostream& os, T const& first, Ts const&... rest) {
    os << first;

    if constexpr (sizeof...(rest) > 0) {        
        // this line will only be instantiated if there are further
        // arguments. if rest... is empty, there will be no call to
        // print_all(os). 
        print_all(os, rest...);
    }
}