C ++使用模板参数包来调用多个模板化函数

时间:2016-05-27 18:19:44

标签: c++ templates c++14

亲爱的互联网人士,

我目前正在C ++ 14中编写一个变体类,需要为参数包中的每个元素调用一个模板函数。经过一些搜索,我遇到了this页面,其中包含以下示例。

template<typename... Ts> void func(Ts... args){
    const int size = sizeof...(args) + 2;
    int res[size] = {1,args...,2};
    // since initializer lists guarantee sequencing, this can be used to
    // call a function on each element of a pack, in order:
    int dummy[sizeof...(Ts)] = { (std::cout << args, 0)... };
}

我创建了这个小例子,展示了我想要实现的目标。

#include <iostream>

template<typename Ta, typename Tb> struct TypeCmp
{
    static constexpr bool Value = false;
};

template<typename T> struct TypeCmp<T, T>
{
    static constexpr bool Value = true;
};

template<typename T, typename... Ts> struct TypeIdCounter;

template<typename T> struct TypeIdCounter<T>
{
    static constexpr size_t Value = 1;
};

template<typename T, typename Tcur, typename... Ts> struct TypeIdCounter<T, Tcur, Ts...>
{
    static constexpr size_t Value = sizeof(Tcur)+(TypeCmp<T, Tcur>::Value ? 0 : TypeIdCounter<T, Ts...>::Value);
};

template<typename... Ts> struct TypeHolder
{
    template<typename T> struct Info
    {
        static constexpr size_t Id = TypeIdCounter<T, Ts...>::Value;
        static constexpr size_t Size = sizeof(T);

        static void Print(size_t id)
        {
            if (Id == id)
            {
                std::cout << "Type::Id = " << Id << std::endl;
                std::cout << "Type::Size = " << Size << std::endl;
            }
        }
    };

    template<typename T> TypeHolder(const T& value) : id(Info<T>::Id)
    {
        /* copy value to container */
    }

    void Print() const
    {
        int dummy[] = {(Info<Ts>::Print(id), 0)...};
        if (dummy[0])
        {
            /* dummy test needed! */
        }
    }

    size_t id;
};

struct Foo
{
    std::string name;
    int age;
};

typedef TypeHolder<int, long long, bool, Foo> MyTypes;

int main(int argc, char* args[])
{
    std::cout << "Id(int): " << MyTypes::Info<int>::Id << std::endl;
    std::cout << "Id(bool): " << MyTypes::Info<bool>::Id << std::endl;
    std::cout << "Id(Foo): " << MyTypes::Info<Foo>::Id << std::endl;

    MyTypes types(true);

    types.Print();

    return 0;
}

上面列出的程序生成以下输出。

Id(int): 4
Id(bool): 13
Id(Foo): 53
Type::Id = 13
Type::Size = 1

与往常一样,我使用-Wall -Werror标志编译我的代码。因此需要if (dummy[0])...条件,否则我会收到unused variable 'dummy'错误。

我完全不知道为什么int dummy[] = {(Info<Ts>::Print(id), 0)...};似乎有效,因为Info::Print是一个void方法。有人可以启发我并解释为什么这有效吗?有没有办法阻止int dummy[] = {(...)}; if (dummy[0]);技巧而不放弃-Wall -Werror

我一直在努力寻找解释,但由于我不知道这种结构甚至被称为什么都不容易找到。

1 个答案:

答案 0 :(得分:2)

using dummy=int[];
(void)dummy{0,
  ((void)(Info<Ts>::Print(id)), 0)...
};

将取代

int dummy[] = {(Info<Ts>::Print(id), 0)...};

并且不应生成任何警告。它还处理0长度列表。

,运算符将采用void类型。它是内置的,而且很神奇。

使用上面(void)旁边的Print可确保Print的返回类型为非 - void返回类型重载operator,(blah, 0),我们不会出现意外行为。

我自己,在支持它的编译器上,我写了invoke_for_each

template<class T>struct tag_type{using type=T;};
template<class T>constexpr tag_type<T> tag{};

template<class F, class...Ts>
void invoke_for_each( F&& f, Ts&&...ts ) {
  using dummy=int[];
  (void)dummy{0,
    ((void)(f(std::forward<Ts>(ts)), 0)...
  };
};

现在正在使用:

invoke_for_each(
  [&](auto tag){
    using T=typename decltype(tag)::type;
    Info<T>::Print(id);
  },
  tag<Ts>...
);

我们将魔术阵列的东西移出阅读代码的人的视线。