Transform a variadic pack of types into values?

痴心易碎 提交于 2019-12-24 15:41:18

问题


I have some function

template<typename T>
constexpr Foo make_foo<T>();

which essentially maps types to instances of Foo with no side-effects.

Now, I want to write the function magic,

template<typename Types...>
vector<Foo> magic();

which does the same as make_foo, but for variadic parameter packs; and in a way which it would then be easy for me to, say, stream all these Foo's to std::cout, or to iterate over them in a loop etc. I realize this question is not entirely well-defined, since I'm not clear if the relevant output I'm looking for is somekind of variadic packs of values (seeing how that doesn't exist in runtime).

So, what's the idiomatic way of doing that? I noticed that Eric Niebler has a blog page about a metaprogramming library which seems to be relevant, but it seems like a bit overkill. For my case.


回答1:


If you need a vector of Foos, then it's just

template<typename Types...>
vector<Foo> magic(){
    return { make_foo<Types>()... };
}

You can also throw them into a local array and play with them however you want:

Foo foos[] = { make_foo<Types>()... };



回答2:


The standard way to access individual members of a parameter pack is via recursion, stripping off one or more members out of a time. In this case, we could make magic() a wrapper function, that passes the parameter pack to a worker function that in turn calls make_foo<T>(). So, assuming make_foo<T>() can only take a single type at a time, we would have something like this.

// Prototypes:
// -----
template<typename T>
constexpr Foo make_foo();

template<typename... Types>
std::vector<Foo> magic();

template<>
std::vector<Foo> magic<>();

template<typename T>
void magic_worker(std::vector<Foo>& vec);

template<typename T, typename U, typename... Types>
void magic_worker(std::vector<Foo>& vec);

// Actual code:
// -----
// The wrapper.
template<typename... Types>
std::vector<Foo> magic() {
    std::vector<Foo> vec;

    magic_worker<Types...>(vec);

    return vec;
}

// Empty parameter pack.
template<>
std::vector<Foo> magic<>() {
    return std::vector<Foo>(0);
}

// Only one type left.
template<typename T>
void magic_worker(std::vector<Foo>& vec) {
    vec.push_back(make_foo<T>());
}

// At least two types left.
template<typename T, typename U, typename... Types>
void magic_worker(std::vector<Foo>& vec) {
    vec.push_back(make_foo<T>());
    magic_worker<U, Types...>(vec);
}

Not sure if this is the most efficient way to do what you want, but it should work. You can output the vector to std::cout via a range-based for loop, if you so desire.

std::vector<Foo> vec = magic</* args */>();

for (auto& a : vec) {
    std::cout << a;
}

For a working example, see here.


Edit: If you're wondering why the recursive version of magic_worker() has two distinct template parameters before the pack, it's to remove ambiguity. As a template pack can have zero members, having one version take <typename T> and the other take <typename T, typename... Types> would confuse the compiler, and prevent your code from compiling. See here for more info.



来源:https://stackoverflow.com/questions/34951058/transform-a-variadic-pack-of-types-into-values

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!