【问题标题】:How do I print out the arguments of a function using a variadic template?如何使用可变参数模板打印出函数的参数?
【发布时间】:2012-09-02 18:50:36
【问题描述】:

这个例子使用了一个通用的可变参数模板和函数。我想打印出传递给f的参数:

#include <iostream>

template <typename T>
void print(T t) 
{
    std::cout << t << std::endl;
}

template <typename...T>
void f(T &&...args) 
{
    print(args...);
    f(args...);
}

int main() 
{
    f(2, 1, 4, 3, 5);
}

但我收到以下错误:

Compilation finished with errors:<br>
source.cpp: In instantiation of '`void f(T ...)` [with `T = {int, int, int, int, int}`]':<br>
source.cpp:16:20: required from here <br>
source.cpp:10:4: error: no matching function for call to '`print(int&, int&, int&, int&, int&)`'<br>
source.cpp:10:4: note: candidate is:<br>
source.cpp:4:6: note: `template<class T> void print(T)`<br>
source.cpp:4:6: note: template argument deduction/substitution failed: 
source.cpp:10:4: note: candidate expects 1 argument, 5 provided

这实际上是我第一次使用可变参数函数,我并不完全了解如何很好地使用它们。

我也不明白为什么这不起作用以及我能做些什么来帮助它。

【问题讨论】:

标签: c++ templates variadic-templates generic-programming function-templates


【解决方案1】:

更新!

由于这个问题很笼统,而且在 C++17 中你可以做得更好,我想给出两种方法。

解决方案 - 我

使用fold expression,这可能很简单

#include <iostream>
#include <utility>  // std::forward

template<typename ...Args>
constexpr void print(Args&&... args) noexcept
{
   ((std::cout << std::forward<Args>(args) << " "), ...);
}

int main()
{
   print("foo", 10, 20.8, 'c', 4.04f);
}

输出:

foo 10 20.8 c 4.04 

(See Live Online)


解决方案-II

if constexpr 的帮助下,现在我们可以避免为递归variadic template function 提供基本情况/ 0 参数情况。这是因为编译器在编译时丢弃了if constexpr 中的错误语句。

#include <iostream>
#include <utility>  // std::forward

template <typename T, typename...Ts>
constexpr void print(T&& first, Ts&&... rest) noexcept
{
   if constexpr (sizeof...(Ts) == 0)
   {
      std::cout << first;               // for only 1-arguments
   }
   else
   {
      std::cout << first << " ";        // print the 1 argument
      print(std::forward<Ts>(rest)...); // pass the rest further
   }
}

int main()
{
   print("foo", 10, 20.8, 'c', 4.04f);
}

输出

foo 10 20.8 c 4.04

(See Live Online)

【讨论】:

    【解决方案2】:

    你在无限递归。您每次都需要从包中移除一个元素:

    void print() { }                            // 0-argument overload
    
    template <typename Head, typename ...Tail>
    void print(Head const & h, Tail const &... t)         // 1+-argument overload
    {
        std::cout << h;
        print(t...);
    }
    

    你可以用打印包装你的函数调用:

    template <typename ...Args>
    void print_and_f(Args &&... args)
    {
        print(args...);
        f(std::forward<Args>(args)...);
    }
    

    用法:

    print_and_f(1, 2, 3);  // calls "f(1, 2, 3)" eventually.
    

    【讨论】:

    • @David:这是一个强制转换函数。我以为你想要那个,因为你的普遍参考。不过,您可以删除它;它不应该对打印产生任何影响。
    • @David:我已经修改了答案以满足您的函数调用f。不过,我不会真正使用此解决方案,而是将打印直接烘焙到实际函数 f 中。
    【解决方案3】:

    给你。您的代码中有几个错误,您可以在下面的行之间看到 cmets:

    #include <iostream>
    
    template <typename T>
    void print(T t) {
       std::cout << t << std::endl;
    }
    
    // Base case, no args
    void f() {}
    
    // Split the parameter pack.
    // We want the first argument, so we can print it.
    // And the rest so we can forward it to the next call to f
    template <typename T, typename...Ts>
    void f(T &&first, Ts&&... rest) {
        // print it
        print(std::forward<T>(first));
        // Forward the rest.
        f(std::forward<Ts>(rest)...);
    }
    
    int main() {
        f(2, 1, 4, 3, 5);
    }
    

    请注意,在这里使用右值引用是没有意义的。您没有将参数存储在任何地方,因此只需通过 const 引用传递它们就可以了。这样你也可以避免使用std::forward 来保持(无用的)完美转发。

    因此,您可以将f 改写如下:

    template <typename T, typename...Ts>
    void f(const T &first, const Ts&... rest) {
        print(first);
        f(rest...);
    }
    

    【讨论】:

    猜你喜欢
    • 2013-06-04
    • 2021-10-01
    • 2021-05-19
    • 1970-01-01
    • 2016-10-05
    • 2022-01-14
    • 1970-01-01
    • 2016-12-01
    • 1970-01-01
    相关资源
    最近更新 更多