可变参数模板

发布于 2024-09-18 01:13:07 字数 81 浏览 5 评论 0原文

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

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

I have seen a lot of links introducing the variadic templates. But I have never seen any compilable example that demonstrates this approach.

Could someone provide me with some links in which such compilable examples can be found?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(8

伴梦长久 2024-09-25 01:13:07

最简单的示例之一是以下 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";
}

One of the simplest possible examples is the following implementation of max which isn't even templated on types.

int maximum(int n)
{
    return n;
}

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

Only slightly more complex is the canonical printf implementation:

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";
}
秉烛思 2024-09-25 01:13:07

可变参数模板是一项 C++0x 功能,主要针对通用库的作者。我不希望在“用户代码”中看到它们。 ,在 C++0x 标准库中,它们在很多地方使用:std::function、std::async、std::reference_wrapper、std::tuple、std::packaged_task...

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

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

Variadic templates are a C++0x feature that primarily targets authors of generic libraries. I would not expect to see them in "user code". For example, in the C++0x standard library they are used in a lot of places: std::function, std::async, std::reference_wrapper, std::tuple, std::packaged_task, ...

To give you an example I'll show you how a reference_wrapper might be implemented with respect to variadic templates:

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)...);
    }
};

This is not perfectly conforming to the standard draft but it is supposed to be compilable with little modification. It demonstrates multiple C++0x features:

  • deleted functions (disabling the constructor for rvalues)
  • rvalue references (detecting rvalue arguments to the constructor, perfect forwarding)
  • type deduction via decltype
  • standard library function template declval to create objects for the purpose of building an expression for decltype (GCC does not yet offer this function template. You have to write it yourself)
  • variadic templates (accepting an arbitrary number of parameters)

The purpose of the variadic member template is to forward arguments to the object referred to by ptr. This should work in case T is a function pointer type or a class type with overloaded function call operator.

cheers!
s

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

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

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

如果你仔细遵循了第 3 点,你一定已经意识到 print(T t, Args. ..args) 无法处理 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;}

所以关于...

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

A very simple example of variadic template:

Suppose we want to have a function which takes variable number of arguments and prints them all. For ex:

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

For that functionality to work, we would basically require two functions:

First one, a function which takes variable number of arguments:

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

Some explanation:

1.) Parameter Packs denoted by ellipsis(...), that appear in parameter list.

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

That means, these all are same.

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

So, you don't have to worry about the correct position of the whitespace in there. Though, IMO at most one whitespace should be used as a best practice.

2.) Pack Expansion: A pattern followed by an ellipsis.

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

3.) Parameter pack accepts zero or more template args. So, print(T t, Args... args) accepts one or more args.


Once you understand that, we can visualize the call flow as below:

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

translates into:

print(string, int, float, long);

which calls

print(int, float, long);

which calls

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

which calls

print(long);         // say Level 1

which calls

print();             // say Level 0

If you have followed the Point#3 carefully, you must have realized that print(T t, Args... args) can't handle call at Level 0.
So we need another function here with same name to catch up at any level >=0.


Second one, a function to grab the call at the top of call stack:

Catch at level 0:

void print(){}

or, Catch at level 1:

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

or, Catch at level 2:

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

so on...

Any of these would work. Hope this helps you next time you go about writing such function or class.

一身仙ぐ女味 2024-09-25 01:13:07

这是我在博客上发布的可变参数模板的示例:
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;
};

This is an example of variadic templates that I put up on my blog:
http://thenewcpp.wordpress.com/2011/11/23/variadic-templates-part-1-2/

It compiles. It demonstrates finding the largest type from a group of types.

#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;
};
薄荷港 2024-09-25 01:13:07

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

Variadic templates are part of the C++0x standard which is not yet officially released. They are supported by gcc since version 4.3, but you need to enable support for C++0x by adding the compiler switch -std=c++0x.

瑕疵 2024-09-25 01:13:07

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

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

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

即从C++11开始你只能编写一个模板,编译器将自行生成所需的函数。

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

Before C++11, you can create template only with the fixed count of parameters.

Firts template for the function with one parameter.

Second template for the function with two parameters.
... i.e.

Since C++11 you can write only one template, compiler will generate required function itself.

Good example
http://eli.thegreenplace.net/2014/variadic-templates-in-c/

合久必婚 2024-09-25 01:13:07

另一种语法:扩展,例如,

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> >

another syntax: expanding, e.g.

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

hence:

MyMaps<int,int,string>:Maps

is now actually:

std::tuple<std::map<int,int>,std::map<string,int> >
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文