如何对可变参数模板函数的异构参数包进行通用计算?


77

前提:

在使用了可变参数模板后,我意识到,要完成琐碎的元编程任务之外的任何事情,很快就会变得很麻烦。特别是,我发现自己希望的执行方式上的参数包一般操作迭代拆分循环在一个std::for_each样的方式,等等。

在观看了C ++和Beyond 2012的Andrei Alexandrescu的关于static ifC ++(从D编程语言中借用的结构)的可取性的演讲之后,我感到有些东西static for也将派上用场-我感到更多这样的static结构可以带来利益。

于是我开始想知道如果有一种方法来实现这样的一个可变参数模板函数的参数包(伪代码):

template<typename... Ts>
void my_function(Ts&&... args)
{
    static for (int i = 0; i < sizeof...(args); i++) // PSEUDO-CODE!
    {
        foo(nth_value_of<i>(args));
    }
}

它将在编译时转换为如下形式:

template<typename... Ts>
void my_function(Ts&&... args)
{
    foo(nth_value_of<0>(args));
    foo(nth_value_of<1>(args));
    // ...
    foo(nth_value_of<sizeof...(args) - 1>(args));
}

原则上,static_for将允许进行更精细的处理:

template<typename... Ts>
void foo(Ts&&... args)
{
    constexpr s = sizeof...(args);

    static for (int i = 0; i < s / 2; i++)
    {
        // Do something
        foo(nth_value_of<i>(args));
    }

    static for (int i = s / 2; i < s; i++)
    {
        // Do something different
        bar(nth_value_of<i>(args));
    }
}

或像这样的更具表现力的习语:

template<typename... Ts>
void foo(Ts&&... args)
{
    static for_each (auto&& x : args)
    {
        foo(x);
    }
}

相关工作:

我在网上做了一些搜索,发现确实存在一些东西

  • 该链接描述了如何将参数包转换为Boost.MPL向量,但这仅达到目标的一半(如果不是更少的话)。
  • 关于SO的这个问题似乎需要类似且略相关的元编程功能(将参数包分成两半)-实际上,关于SO的几个问题似乎与此问题相关,但是我没有答案阅读完满意地恕我直言;
  • Boost.Fusion定义了将参数包转换为元组的算法,但我希望:
    1. 不创建不必要的临时变量来保存可以(并且应该)完美地转发给某些通用算法的参数;
    2. 有一个小型的,自包含的库可以执行此操作,而Boost.Fusion可能包含的内容远远超过解决此问题所需的内容。

题:

是否有一个相对简单的方法(可能通过一些模板元编程)来实现我正在寻找的目标,而又不会受到现有方法的限制?


如果foo返回了某些内容,则可以编写eat(foo(args)...),其中eat是一个对其参数不执行任何操作的函数。需要对返回void的函数进行一些调整,或者如果您想指定执行顺序(已经在usenet上进行了讨论,可能是comp.lang.c ++。moderated,尽管我目前无法找到它)。它被讨论,让foo(args);...
马克·Glisse

@MarcGlisse:您有一个观点,的确我尝试过。该解决方案的问题在于C ++不保证函数参数的求值顺序,这在迭代时通常是需要的(除了事实,即使不需要函数,函数也必须返回值,但这是次要的)。
Andy Prowl 2013年

我认为有一个变体,其中指定了参数的求值顺序,也许在{}(初始化列表)中。至于返回类型,您可以执行(foo(args),0)...或其他技巧。
Marc Glisse 2013年

@MarcGlisse:可能是这样,如果您愿意,请随时尝试并改进我的图书馆。如果能做得更好我会很高兴。坦率地说,我认为我的解决方案不会带来任何开销,也不会在客户端上施加任何约束,这对我而言是很好的。但这当然并不意味着它是完美的
Andy Prowl

1
FWIW,过去我已经验证了GCC和VC ++可以完全优化发布版本中的引用元组–不使用元组相同的代码生成(使用Boost.Fusion测试)。
ildjarn

Answers:


65

由于我对发现的结果不满意,因此我尝试自己制定解决方案,并最终编写了一个小型库,该允许对参数包进行通用运算。我的解决方案具有以下功能:

  • 允许迭代参数包的全部或某些元素,可能通过计算它们在参数包上的索引来指定;
  • 允许将参数包的计算部分转发给可变参函数;
  • 只需要包含一个相对较短的头文件;
  • 广泛使用完美的转发以进行大量内联,并避免不必要的复制/移动,以将性能损失降至最低;
  • 迭代算法的内部实现依靠空基类优化来最大程度地减少内存消耗。
  • 扩展和适应很容易(相对而言,考虑到它是模板元编程)。

我将首先展示如何使用该库,然后发布其实现

用例

这是一个示例,说明如何使用该for_each_in_arg_pack()函数迭代包的所有参数并将输入中的每个参数传递给客户端提供的某些函子(当然,如果参数包包含值,则函子必须具有通用调用运算符异构类型):

// Simple functor with a generic call operator that prints its input. This is used by the
// following functors and by some demonstrative test cases in the main() routine.
struct print
{
    template<typename T>
    void operator () (T&& t)
    {
        cout << t << endl;
    }
};

// This shows how a for_each_*** helper can be used inside a variadic template function
template<typename... Ts>
void print_all(Ts&&... args)
{
    for_each_in_arg_pack(print(), forward<Ts>(args)...);
}

print上面的函子也可以用于更复杂的计算中。特别是,这是一个如何迭代包中参数的子集(在这种情况下,是子范围)的方式:

// Shows how to select portions of an argument pack and 
// invoke a functor for each of the selected elements
template<typename... Ts>
void split_and_print(Ts&&... args)
{
    constexpr size_t packSize = sizeof...(args);
    constexpr size_t halfSize = packSize / 2;

    cout << "Printing first half:" << endl;
    for_each_in_arg_pack_subset(
        print(), // The functor to invoke for each element
        index_range<0, halfSize>(), // The indices to select
        forward<Ts>(args)... // The argument pack
        );

    cout << "Printing second half:" << endl;
    for_each_in_arg_pack_subset(
        print(), // The functor to invoke for each element
        index_range<halfSize, packSize>(), // The indices to select
        forward<Ts>(args)... // The argument pack
        );
}

有时,人们可能只想将参数包的一部分转发给其他可变参数函子,而不是遍历其元素并将每个参数分别传递给非可变函子。这是forward_subpack()算法允许执行的操作:

// Functor with variadic call operator that shows the usage of for_each_*** 
// to print all the arguments of a heterogeneous pack
struct my_func
{
    template<typename... Ts>
    void operator ()(Ts&&... args)
    {
        print_all(forward<Ts>(args)...);
    }
};

// Shows how to forward only a portion of an argument pack 
// to another variadic functor
template<typename... Ts>
void split_and_print(Ts&&... args)
{
    constexpr size_t packSize = sizeof...(args);
    constexpr size_t halfSize = packSize / 2;

    cout << "Printing first half:" << endl;
    forward_subpack(my_func(), index_range<0, halfSize>(), forward<Ts>(args)...);

    cout << "Printing second half:" << endl;
    forward_subpack(my_func(), index_range<halfSize, packSize>(), forward<Ts>(args)...);
}

对于更具体的任务,当然可以通过索引来检索包中的特定参数。这是该nth_value_of()函数及其助手first_value_of()和允许执行的操作last_value_of()

// Shows that arguments in a pack can be indexed
template<unsigned I, typename... Ts>
void print_first_last_and_indexed(Ts&&... args)
{
    cout << "First argument: " << first_value_of(forward<Ts>(args)...) << endl;
    cout << "Last argument: " << last_value_of(forward<Ts>(args)...) << endl;
    cout << "Argument #" << I << ": " << nth_value_of<I>(forward<Ts>(args)...) << endl;
}

另一方面,如果自变量包是同构的(即所有自变量具有相同的类型),则可能更希望使用以下公式。所述is_homogeneous_pack<>元函数允许确定在参数包的所有类型是否是同质的,并且主要是指在所使用static_assert()的语句:

// Shows the use of range-based for loops to iterate over a
// homogeneous argument pack
template<typename... Ts>
void print_all(Ts&&... args)
{
    static_assert(
        is_homogeneous_pack<Ts...>::value, 
        "Template parameter pack not homogeneous!"
        );

    for (auto&& x : { args... })
    {
        // Do something with x...
    }

    cout << endl;
}

最后,由于lambda只是函子的语法糖,因此它们也可以与上述算法结合使用。但是,直到C ++支持通用lambda为止,这仅适用于同类参数包。以下示例还显示了homogeneous-type<>元函数的用法,该元函数返回同构包中所有参数的类型:

 // ...
 static_assert(
     is_homogeneous_pack<Ts...>::value, 
     "Template parameter pack not homogeneous!"
     );
 using type = homogeneous_type<Ts...>::type;
 for_each_in_arg_pack([] (type const& x) { cout << x << endl; }, forward<Ts>(args)...);

这基本上是库允许的操作,但是我相信它甚至可以扩展来执行更复杂的任务。

实施方式

现在是实现,它本身有点棘手,因此我将依靠注释来解释代码,并避免将本文过长(也许已经很久了):

#include <type_traits>
#include <utility>

//===============================================================================
// META-FUNCTIONS FOR EXTRACTING THE n-th TYPE OF A PARAMETER PACK

// Declare primary template
template<int I, typename... Ts>
struct nth_type_of
{
};

// Base step
template<typename T, typename... Ts>
struct nth_type_of<0, T, Ts...>
{
    using type = T;
};

// Induction step
template<int I, typename T, typename... Ts>
struct nth_type_of<I, T, Ts...>
{
    using type = typename nth_type_of<I - 1, Ts...>::type;
};

// Helper meta-function for retrieving the first type in a parameter pack
template<typename... Ts>
struct first_type_of
{
    using type = typename nth_type_of<0, Ts...>::type;
};

// Helper meta-function for retrieving the last type in a parameter pack
template<typename... Ts>
struct last_type_of
{
    using type = typename nth_type_of<sizeof...(Ts) - 1, Ts...>::type;
};

//===============================================================================
// FUNCTIONS FOR EXTRACTING THE n-th VALUE OF AN ARGUMENT PACK

// Base step
template<int I, typename T, typename... Ts>
auto nth_value_of(T&& t, Ts&&... args) ->
    typename std::enable_if<(I == 0), decltype(std::forward<T>(t))>::type
{
    return std::forward<T>(t);
}

// Induction step
template<int I, typename T, typename... Ts>
auto nth_value_of(T&& t, Ts&&... args) ->
    typename std::enable_if<(I > 0), decltype(
        std::forward<typename nth_type_of<I, T, Ts...>::type>(
            std::declval<typename nth_type_of<I, T, Ts...>::type>()
            )
        )>::type
{
    using return_type = typename nth_type_of<I, T, Ts...>::type;
    return std::forward<return_type>(nth_value_of<I - 1>((std::forward<Ts>(args))...));
}

// Helper function for retrieving the first value of an argument pack
template<typename... Ts>
auto first_value_of(Ts&&... args) ->
    decltype(
        std::forward<typename first_type_of<Ts...>::type>(
            std::declval<typename first_type_of<Ts...>::type>()
            )
        )
{
    using return_type = typename first_type_of<Ts...>::type;
    return std::forward<return_type>(nth_value_of<0>((std::forward<Ts>(args))...));
}

// Helper function for retrieving the last value of an argument pack
template<typename... Ts>
auto last_value_of(Ts&&... args) ->
    decltype(
        std::forward<typename last_type_of<Ts...>::type>(
            std::declval<typename last_type_of<Ts...>::type>()
            )
        )
{
    using return_type = typename last_type_of<Ts...>::type;
    return std::forward<return_type>(nth_value_of<sizeof...(Ts) - 1>((std::forward<Ts>(args))...));
}

//===============================================================================
// METAFUNCTION FOR COMPUTING THE UNDERLYING TYPE OF HOMOGENEOUS PARAMETER PACKS

// Used as the underlying type of non-homogeneous parameter packs
struct null_type
{
};

// Declare primary template
template<typename... Ts>
struct homogeneous_type;

// Base step
template<typename T>
struct homogeneous_type<T>
{
    using type = T;
    static const bool isHomogeneous = true;
};

// Induction step
template<typename T, typename... Ts>
struct homogeneous_type<T, Ts...>
{
    // The underlying type of the tail of the parameter pack
    using type_of_remaining_parameters = typename homogeneous_type<Ts...>::type;

    // True if each parameter in the pack has the same type
    static const bool isHomogeneous = std::is_same<T, type_of_remaining_parameters>::value;

    // If isHomogeneous is "false", the underlying type is the fictitious null_type
    using type = typename std::conditional<isHomogeneous, T, null_type>::type;
};

// Meta-function to determine if a parameter pack is homogeneous
template<typename... Ts>
struct is_homogeneous_pack
{
    static const bool value = homogeneous_type<Ts...>::isHomogeneous;
};

//===============================================================================
// META-FUNCTIONS FOR CREATING INDEX LISTS

// The structure that encapsulates index lists
template <unsigned... Is>
struct index_list
{
};

// Collects internal details for generating index ranges [MIN, MAX)
namespace detail
{
    // Declare primary template for index range builder
    template <unsigned MIN, unsigned N, unsigned... Is>
    struct range_builder;

    // Base step
    template <unsigned MIN, unsigned... Is>
    struct range_builder<MIN, MIN, Is...>
    {
        typedef index_list<Is...> type;
    };

    // Induction step
    template <unsigned MIN, unsigned N, unsigned... Is>
    struct range_builder : public range_builder<MIN, N - 1, N - 1, Is...>
    {
    };
}

// Meta-function that returns a [MIN, MAX) index range
template<unsigned MIN, unsigned MAX>
using index_range = typename detail::range_builder<MIN, MAX>::type;

//===============================================================================
// CLASSES AND FUNCTIONS FOR REALIZING LOOPS ON ARGUMENT PACKS

// Implementation inspired by @jogojapan's answer to this question:
// http://stackoverflow.com/questions/14089637/return-several-arguments-for-another-function-by-a-single-function

// Collects internal details for implementing functor invocation
namespace detail
{
    // Functor invocation is realized through variadic inheritance.
    // The constructor of each base class invokes an input functor.
    // An functor invoker for an argument pack has one base class
    // for each argument in the pack

    // Realizes the invocation of the functor for one parameter
    template<unsigned I, typename T>
    struct invoker_base
    {
        template<typename F, typename U>
        invoker_base(F&& f, U&& u) { f(u); }
    };

    // Necessary because a class cannot inherit the same class twice
    template<unsigned I, typename T>
    struct indexed_type
    {
        static const unsigned int index = I;
        using type = T;
    };

    // The functor invoker: inherits from a list of base classes.
    // The constructor of each of these classes invokes the input
    // functor with one of the arguments in the pack.
    template<typename... Ts>
    struct invoker : public invoker_base<Ts::index, typename Ts::type>...
    {
        template<typename F, typename... Us>
        invoker(F&& f, Us&&... args)
            :
            invoker_base<Ts::index, typename Ts::type>(std::forward<F>(f), std::forward<Us>(args))...
        {
        }
    };
}

// The functor provided in the first argument is invoked for each
// argument in the pack whose index is contained in the index list
// specified in the second argument
template<typename F, unsigned... Is, typename... Ts>
void for_each_in_arg_pack_subset(F&& f, index_list<Is...> const& i, Ts&&... args)
{
    // Constructors of invoker's sub-objects will invoke the functor.
    // Note that argument types must be paired with numbers because the
    // implementation is based on inheritance, and one class cannot
    // inherit the same base class twice.
    detail::invoker<detail::indexed_type<Is, typename nth_type_of<Is, Ts...>::type>...> invoker(
        f,
        (nth_value_of<Is>(std::forward<Ts>(args)...))...
        );
}

// The functor provided in the first argument is invoked for each
// argument in the pack
template<typename F, typename... Ts>
void for_each_in_arg_pack(F&& f, Ts&&... args)
{
    for_each_in_arg_pack_subset(f, index_range<0, sizeof...(Ts)>(), std::forward<Ts>(args)...);
}

// The functor provided in the first argument is given in input the
// arguments in whose index is contained in the index list specified
// as the second argument.
template<typename F, unsigned... Is, typename... Ts>
void forward_subpack(F&& f, index_list<Is...> const& i, Ts&&... args)
{
    f((nth_value_of<Is>(std::forward<Ts>(args)...))...);
}

// The functor provided in the first argument is given in input all the
// arguments in the pack.
template<typename F, typename... Ts>
void forward_pack(F&& f, Ts&&... args)
{
    f(std::forward<Ts>(args)...);
}

结论

当然,即使我对这个问题提供了自己的答案(实际上是因为这个事实),但我很想知道是否存在我错过的替代或更好的解决方案-除了“相关作品”部分中提到的解决方案之外这个问题。


4
老实说,这值得比它多。我喜欢的想法static if,并static for作为对我来说,他们会元编程容易得多读,写和理解,但我不介意,如果这些都在标准库中实现。
克里斯,

2
@chris:实际上,大多数功能都可以通过Boost.Fusion来实现,方法是先将参数包转换为元组(在我撰写本文时,我不知道编译器会优化元组)。但是感谢您的赞赏:)
Andy Prowl

嗯,我不知道Boost Fusion可以做到。老实说,我对Boost的了解很少(我只有在配备了C ++ 11之后才发现),但是Fusion库以及很多其他库激发了我的兴趣。
克里斯,

我知道这个答案是三年前写的,但是我希望您能回答我:为什么不编写last_type_ofO(1)实现?任何人都可以编写1024个param函数(即,我不是在谈论template-deph问题),但是您可以减少编译时间。谢谢。
Manu343726

@ Manu343726:那是6个月前,而不是3年:DI并不关心复杂性,因为它都是在编译时完成的,因此通常不必担心计算复杂性。我刚刚写了一个想到的最简单的实现方法:)
Andy Prowl

10

根据讨论,让我发布以下代码:

#include <initializer_list>
#define EXPAND(EXPR) std::initializer_list<int>{((EXPR),0)...}

// Example of use:
#include <iostream>
#include <utility>

void print(int i){std::cout << "int: " << i << '\n';}
int print(double d){std::cout << "double: " << d << '\n';return 2;}

template<class...T> void f(T&&...args){
  EXPAND(print(std::forward<T>(args)));
}

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

我检查与生成的代码g++ -std=c++11 -O1main只包含3个呼叫print,没有扩张助手的痕迹。


感谢您抽出时间来尝试回答我的问题。目前,我对您的解决方案有一个评论(并不意味着它不能解决):它不使用完美的转发。使用intdoubles并没有多大关系,但是使用UDT意味着它会生成副本和移动。而且您不能更改EXPAND(print(args))为EXPAND(print(forward <T>(args))),因为宏预处理器会大声说些不礼貌的事情。
Andy Prowl 2013年

我只是添加了forward <T>(谢谢,我忘记添加它了),并且宏预处理器根本没有抱怨……
Marc Glisse 2013年

没错,由于某种原因,我认为预处理器不喜欢尖括号。不知道为什么。因此,对于要在范围的所有元素上进行迭代的较简单的情况,我想这是我的方法的有效替代方法。它还具有的优点是,它允许所调用的函数成为函数模板,而不仅仅是我的解决方案所要求的函子。
安迪·普罗

我将接受我自己的答案,因为我相信这是涵盖问题更一般性的内容(如何对参数包执行通用运算)的答案。您的技术非常有趣,因为它很紧凑并且不需要函子,但是它适用于整个参数列表,并且不允许轻易拆分数据包或选择子包来进行迭代。不过绝对是+1
Andy Prowl

好。注意,这两种方法可以轻松组合。向前转发一次以使index_range可用,然后使用EXPAND执行涉及该对象及其索引的任何代码(它不介意同时扩展2个包)。
Marc Glisse 2013年

5

使用枚举解决方案(ala Python)。

用法:

void fun(int i, size_t index, size_t size) {
    if (index != 0) {
        std::cout << ", ";
    }

    std::cout << i;

    if (index == size - 1) {
        std::cout << "\n";
    }
} // fun

enumerate(fun, 2, 3, 4);

// Expected output: "2, 3, 4\n"
// check it at: http://liveworkspace.org/code/1cydbw$4

码:

// Fun: expects a callable of 3 parameters: Arg, size_t, size_t
// Arg: forwarded argument
// size_t: index of current argument
// size_t: number of arguments
template <typename Fun, typename... Args, size_t... Is>
void enumerate_impl(Fun&& fun, index_list<Is...>, Args&&... args) {
    std::initializer_list<int> _{
        (fun(std::forward<Args>(args), Is, sizeof...(Is)), 0)...
    };
    (void)_; // placate compiler, only the side-effects interest us
}

template <typename Fun, typename... Args>
void enumerate(Fun&& fun, Args&&... args) {
    enumerate_impl(fun,
                   index_range<0, sizeof...(args)>(),
                   std::forward<Args>(args)...);
}

范围生成器(从您的解决方案中窃取):

// The structure that encapsulates index lists
template <size_t... Is>
struct index_list
{
};

// Collects internal details for generating index ranges [MIN, MAX)
namespace detail
{
    // Declare primary template for index range builder
    template <size_t MIN, size_t N, size_t... Is>
    struct range_builder;

    // Base step
    template <size_t MIN, size_t... Is>
    struct range_builder<MIN, MIN, Is...>
    {
        typedef index_list<Is...> type;
    };

    // Induction step
    template <size_t MIN, size_t N, size_t... Is>
    struct range_builder : public range_builder<MIN, N - 1, N - 1, Is...>
    {
    };
}

// Meta-function that returns a [MIN, MAX) index range
template<size_t MIN, size_t MAX>
using index_range = typename detail::range_builder<MIN, MAX>::type;

@AndyProwl:我必须承认,在过去的几年中,我对多个Python“标准”函数的优雅感到惊讶不止一次。这个enumerate例子是我真的很想念的东西,尽管我想通常会更多,for (auto p: enumerate(container))而且我不确定这两个版本(容器迭代和元组迭代)是否可以很好地兼容:)
Matthieu M.13年

我必须在这里可耻地承认我对Python的巨大无知:-)好吧,看来我应该开始研究它了
Andy Prowl

@AndyProwl:函数枚举和模块itertools是很好的起点:)
Matthieu M.

0

...符号确实有一些有趣的选项,例如:

template<typename T>
int print(const T& x) {
  std::cout << "<" << x << ">";
  return 0;
}

void pass(...) {}

template<typename... TS>
void printall(TS... ts){
  pass(print(ts)...);
}

不幸的是,我不知道有什么方法可以强制执行打印函数的调用顺序(在我的编译器上是相反的)。请注意,打印需要返回某些内容。

如果您不关心订单,此技巧很有用。


参见gitorious.org/redistd/redistd/blobs/master/include/redi/…以一种强制执行排序的方法,使用标准的功能编程技巧打印参数包的头部,然后递归处理尾部
Jonathan Wakely

0

在阅读了一些其他文章并进行了一段时间的修改之后,我想到了以下内容(与上面的内容有些相似,但是实现有些不同)。我是使用Visual Studio 2013编译器编写的。

使用Lambda表达式的用法-

static_for_each()(
    [](std::string const& str)
    {
        std::cout << str << std::endl;
    }, "Hello, ", "Lambda!");

使用lambda的缺点是参数必须与lambda的参数列表中声明的类型相同。这意味着它将仅适用于一种类型。如果要使用模板化功能,则可以使用下一个示例。

使用struct wrapper functor的用法-

struct print_wrapper
{
    template <typename T>
    void operator()(T&& str)
    {
        std::cout << str << " ";
    }
};

// 
// A little test object we can use.
struct test_object
{
    test_object() : str("I'm a test object!") {}
    std::string str;
};

std::ostream& operator<<(std::ostream& os, test_object t)
{
    os << t.str;
    return os;
}

//
// prints: "Hello, Functor! 1 2 I'm a test object!"
static_for_each()(print_wrapper(), "Hello,", "Functor!", 1, 2.0f, test_object());

这样,您就可以传入所需的任何类型,并使用函子对其进行操作。我发现这很干净,并且可以很好地满足我的需求。您也可以将其与功能参数包一起使用,例如:

template <typename T, typename... Args>
void call(T f, Args... args)
{
    static_for_each()(f, args...);
}

call(print_wrapper(), "Hello", "Call", "Wrapper!");

这是实现-

// 
// Statically iterate over a parameter pack 
// and call a functor passing each argument.
struct static_for_each
{
private:
    // 
    // Get the parameter pack argument at index i.
    template <size_t i, typename... Args>
    static auto get_arg(Args&&... as) 
    -> decltype(std::get<i>(std::forward_as_tuple(std::forward<Args>(as)...)))
    {
        return std::get<i>(std::forward_as_tuple(std::forward<Args>(as)...));
    }

    //
    // Recursive template for iterating over 
    // parameter pack and calling the functor.
    template <size_t Start, size_t End>
    struct internal_static_for
    {
        template <typename Functor, typename... Ts>
        void operator()(Functor f, Ts&&... args)
        {
            f(get_arg<Start>(args...));
            internal_static_for<Start + 1, End>()(f, args...);
        }
    };

    //
    // Specialize the template to end the recursion.
    template <size_t End>
    struct internal_static_for<End, End>
    {
        template <typename Functor, typename... Ts>
        void operator()(Functor f, Ts&&... args){}
    };

public:
    // 
    // Publically exposed operator()(). 
    // Handles template recursion over parameter pack.
    // Takes the functor to be executed and a parameter 
    // pack of arguments to pass to the functor, one at a time.
    template<typename Functor, typename... Ts>
    void operator()(Functor f, Ts&&... args)
    {
        // 
        // Statically iterate over parameter
        // pack from the first argument to the
        // last, calling functor f with each 
        // argument in the parameter pack.
        internal_static_for<0u, sizeof...(Ts)>()(f, args...);
    }
};

希望人们觉得这有用:-)

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.