23

我看过很多介绍可变参数模板的链接。但我从未见过任何演示这种方法的可编译示例。

有人可以为我提供一些可以找到此类可编译示例的链接吗?

4

8 回答 8

28

最简单的示例之一是以下实现,max它甚至没有在类型上进行模板化。

int maximum(int n)
{
    return n;
}

template<typename... Args>
int maximum(int n, Args... args)
{
    return max(n, maximum(args...));
}

printf规范实现只是稍微复杂一点:

void printf(const char *s)
{
  while (*s)
  {
    if (*s == '%' && *(++s) != '%')
      throw "invalid format string: missing arguments";
    std::cout << *s++;
  }
}

template<typename T, typename... Args>
void printf(const char* s, T value, Args... args)
{
  while (*s)
  {
    if (*s == '%' && *(++s) != '%')
    {
      std::cout << value;
      printf(s, args...); // call even when *s == 0 to detect extra arguments
      return;
    }
    std::cout << *s++;
  }
  throw "extra arguments provided to printf";
}
于 2010-09-05T07:23:53.430 回答
11

可变参数模板是一个 C++0x 特性,主要针对泛型库的作者。我不希望在“用户代码”中看到它们。例如,在 C++0x 标准库中,它们被用于很多地方:std::function、std::async、std::reference_wrapper、std::tuple、std::packaged_task、...

举个例子,我将向您展示如何针对可变参数模板实现 reference_wrapper:

template<class T>
class reference_wrapper
{
    T *ptr;
public:
    explicit reference_wrapper(T& thing) : ptr(&thing) {}
    explicit reference_wrapper(T&&     ) = delete;

    operator T&() const {return *ptr;}

    template<class... Args>
    decltype( declval<T&>()(declval<Args>()...) )
    operator()(Args&&... args) const
    {
        return (*ptr)(forward<Args>(args)...);
    }
};

这并不完全符合标准草案,但它应该是可以编译的,几乎不需要修改。它演示了多个 C++0x 特性:

  • 删除的函数(禁用右值的构造函数)
  • 右值引用(检测构造函数的右值参数,完美转发)
  • 类型扣除通过decltype
  • 标准库函数模板declval,用于创建对象以构建表达式decltype(GCC 尚未提供此函数模板。您必须自己编写)
  • 可变参数模板(接受任意数量的参数)

可变成员模板的目的是将参数转发给ptr. 如果 T 是函数指针类型或具有重载函数调用运算符的类类型,这应该可以工作。

干杯!s

于 2010-09-03T15:44:28.523 回答
6

一个非常简单的可变参数模板示例:

假设我们想要一个函数,它接受可变数量的参数并将它们全部打印出来。例如:

print("Hello", 1, 3.14, 5L);

要使该功能正常工作,我们基本上需要两个功能:

第一个,一个接受可变数量参数的函数:

template<typename T, typename... Args>
void print(T t, Args ...args){
     std::cout << t << ", ";
     print(args...);
}

一些解释:

1.) 用省略号(...) 表示的参数包,出现在参数列表中。

typename...Args 
        |  | << Optional whitespace. Can have multiple whitespaces in between them
    Args...args

也就是说,这些都是一样的。

typename ...args
typename...args
typename   ...   args

因此,您不必担心其中空白的正确位置。不过,IMO 最多应该使用一个空格作为最佳实践。

2.) 包扩展:后跟省略号的模式。

print(args...); //expand when you wish to use them

3.) 参数包接受零个或多个模板参数。因此,print(T t, Args... args)接受一个或多个参数。


一旦你理解了这一点,我们可以将调用流程可视化如下:

print("Hello", 1, 3.14, 5L);

翻译成:

print(string, int, float, long);

调用

print(int, float, long);

调用

print(float, long);  // say Level 2

调用

print(long);         // say Level 1

调用

print();             // say Level 0

如果您仔细遵循了Point#3,您一定已经意识到print(T t, Args... args)无法处理Level 0 的调用。
所以我们需要另一个同名的函数来赶上任何级别> = 0。


第二个,在调用堆栈顶部获取调用的函数:

在 0 级捕获:

void print(){}

或者,在第 1 级赶上:

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

或者,在第 2 级赶上:

template<typename T, typename U>
void print(T t, U u){ std::cout << t << ", " << u;}

很快...

这些中的任何一个都可以。希望这对您下次编写此类函数或类时有所帮助。

于 2017-12-28T07:14:58.703 回答
4

这是我在博客上发布的可变参数模板示例:http: //thenewcpp.wordpress.com/2011/11/23/variadic-templates-part-1-2/

它编译。它演示了从一组类型中找到最大的类型。

#include <type_traits>

template <typename... Args>
struct find_biggest;

//the biggest of one thing is that one thing
template <typename First>
struct find_biggest<First>
{
  typedef First type;
};

//the biggest of everything in Args and First
template <typename First, typename... Args>
struct find_biggest<First, Args...>
{
  typedef typename find_biggest<Args...>::type next;
  typedef typename std::conditional
  <
    sizeof(First) >= sizeof(next),
    First,
    next
  >::type type;
};
于 2011-11-23T21:47:58.187 回答
2

可变参数模板是尚未正式发布的 C++0x 标准的一部分。gcc 从 4.3 版开始支持它们,但您需要通过添加编译器开关 -std=c++0x 来启用对 C++0x 的支持。

于 2010-09-03T11:26:14.723 回答
0

在 C++11 之前,您只能使用固定数量的参数创建模板。

带有一个参数的函数的第一个模板。

具有两个参数的函数的第二个模板。... IE

由于 C++11 只能编写一个模板,编译器会自己生成所需的函数。

很好的例子 http://eli.thegreenplace.net/2014/variadic-templates-in-c/

于 2015-05-19T08:18:19.423 回答
0

另一种语法:扩展,例如

template<typename VAL, typename... KEYS>
class MyMaps
{
  typedef std::tuple< std::map<KEYS,VAL>... > Maps;
}

因此:

MyMaps<int,int,string>:Maps

现在实际上是:

std::tuple<std::map<int,int>,std::map<string,int> >
于 2017-06-14T07:43:36.163 回答